hunter-mate 0.1.7 → 0.1.11
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/assets/extension/background/service-worker.js +808 -153
- package/dist/assets/extension/build-info.json +1 -0
- package/dist/assets/extension/content/index.js +23 -5
- package/dist/assets/extension/manifest.json +5 -4
- package/dist/assets/extension/offscreen/offscreen.html +8 -0
- package/dist/assets/extension/offscreen/offscreen.js +149 -0
- package/dist/assets/extension/package.json +8 -1
- package/dist/assets/extension/sidepanel/index.css +2 -0
- package/dist/assets/extension/sidepanel/index.html +7 -394
- package/dist/assets/extension/sidepanel/index.js +53 -923
- package/dist/assets/server/app.module.js +11 -1
- package/dist/assets/server/app.module.js.map +1 -1
- package/dist/assets/server/auth/auth.middleware.js +9 -1
- package/dist/assets/server/auth/auth.middleware.js.map +1 -1
- package/dist/assets/server/build-info.json +1 -1
- package/dist/assets/server/customer/customer.controller.d.ts +18 -0
- package/dist/assets/server/customer/customer.controller.js +81 -0
- package/dist/assets/server/customer/customer.controller.js.map +1 -0
- package/dist/assets/server/customer/customer.entity.d.ts +17 -0
- package/dist/assets/server/customer/customer.entity.js +82 -0
- package/dist/assets/server/customer/customer.entity.js.map +1 -0
- package/dist/assets/server/customer/customer.module.d.ts +2 -0
- package/dist/assets/server/customer/customer.module.js +28 -0
- package/dist/assets/server/customer/customer.module.js.map +1 -0
- package/dist/assets/server/customer/customer.service.d.ts +36 -0
- package/dist/assets/server/customer/customer.service.js +200 -0
- package/dist/assets/server/customer/customer.service.js.map +1 -0
- package/dist/assets/server/db/schema-validator.service.d.ts +2 -0
- package/dist/assets/server/db/schema-validator.service.js +32 -1
- package/dist/assets/server/db/schema-validator.service.js.map +1 -1
- package/dist/assets/server/db/sqlite.module.d.ts +1 -0
- package/dist/assets/server/db/sqlite.module.js +12 -2
- package/dist/assets/server/db/sqlite.module.js.map +1 -1
- package/dist/assets/server/devops/devops-sink-init.d.ts +1 -14
- package/dist/assets/server/devops/devops-sink-init.js +3 -8
- package/dist/assets/server/devops/devops-sink-init.js.map +1 -1
- package/dist/assets/server/extension/extension-control.controller.d.ts +17 -1
- package/dist/assets/server/extension/extension-control.controller.js +68 -12
- package/dist/assets/server/extension/extension-control.controller.js.map +1 -1
- package/dist/assets/server/extension/extension-control.service.d.ts +6 -1
- package/dist/assets/server/extension/extension-control.service.js +27 -1
- package/dist/assets/server/extension/extension-control.service.js.map +1 -1
- package/dist/assets/server/extension/extension.module.js +2 -1
- package/dist/assets/server/extension/extension.module.js.map +1 -1
- package/dist/assets/server/extraction/adapter.service.d.ts +3 -0
- package/dist/assets/server/extraction/adapter.service.js +127 -6
- package/dist/assets/server/extraction/adapter.service.js.map +1 -1
- package/dist/assets/server/extraction/anomaly-report.service.js +1 -1
- package/dist/assets/server/extraction/anomaly-report.service.js.map +1 -1
- package/dist/assets/server/extraction/entities/adapter-rule.entity.d.ts +2 -0
- package/dist/assets/server/extraction/entities/adapter-rule.entity.js +10 -0
- package/dist/assets/server/extraction/entities/adapter-rule.entity.js.map +1 -1
- package/dist/assets/server/extraction/extraction-entity.controller.d.ts +21 -3
- package/dist/assets/server/extraction/extraction-entity.controller.js +44 -9
- package/dist/assets/server/extraction/extraction-entity.controller.js.map +1 -1
- package/dist/assets/server/extraction/extraction.module.js +13 -3
- package/dist/assets/server/extraction/extraction.module.js.map +1 -1
- package/dist/assets/server/extraction/extraction.service.d.ts +4 -1
- package/dist/assets/server/extraction/extraction.service.js +71 -13
- package/dist/assets/server/extraction/extraction.service.js.map +1 -1
- package/dist/assets/server/extraction/harness-overlay.service.d.ts +41 -0
- package/dist/assets/server/extraction/harness-overlay.service.js +319 -0
- package/dist/assets/server/extraction/harness-overlay.service.js.map +1 -0
- package/dist/assets/server/extraction/liepin-extractor.d.ts +1 -0
- package/dist/assets/server/extraction/liepin-extractor.js +172 -0
- package/dist/assets/server/extraction/liepin-extractor.js.map +1 -0
- package/dist/assets/server/extraction/script-rule.js +14 -2
- package/dist/assets/server/extraction/script-rule.js.map +1 -1
- package/dist/assets/server/governance/action-audit.entity.d.ts +12 -0
- package/dist/assets/server/governance/action-audit.entity.js +66 -0
- package/dist/assets/server/governance/action-audit.entity.js.map +1 -0
- package/dist/assets/server/governance/governance.command-recorder.d.ts +17 -0
- package/dist/assets/server/governance/governance.command-recorder.js +98 -0
- package/dist/assets/server/governance/governance.command-recorder.js.map +1 -0
- package/dist/assets/server/governance/governance.controller.d.ts +14 -0
- package/dist/assets/server/governance/governance.controller.js +77 -0
- package/dist/assets/server/governance/governance.controller.js.map +1 -0
- package/dist/assets/server/governance/governance.ingester.d.ts +21 -0
- package/dist/assets/server/governance/governance.ingester.js +117 -0
- package/dist/assets/server/governance/governance.ingester.js.map +1 -0
- package/dist/assets/server/governance/governance.module.d.ts +2 -0
- package/dist/assets/server/governance/governance.module.js +27 -0
- package/dist/assets/server/governance/governance.module.js.map +1 -0
- package/dist/assets/server/governance/governance.service.d.ts +122 -0
- package/dist/assets/server/governance/governance.service.js +398 -0
- package/dist/assets/server/governance/governance.service.js.map +1 -0
- package/dist/assets/server/jobcase/jobcase.controller.d.ts +1 -0
- package/dist/assets/server/jobcase/jobcase.entity.d.ts +1 -0
- package/dist/assets/server/jobcase/jobcase.entity.js +6 -0
- package/dist/assets/server/jobcase/jobcase.entity.js.map +1 -1
- package/dist/assets/server/jobcase/jobcase.service.d.ts +2 -0
- package/dist/assets/server/jobcase/jobcase.service.js +2 -1
- package/dist/assets/server/jobcase/jobcase.service.js.map +1 -1
- package/dist/assets/server/main.js +1 -0
- package/dist/assets/server/main.js.map +1 -1
- package/dist/assets/server/package.json +1 -1
- package/dist/assets/server/preference/preference.controller.d.ts +14 -0
- package/dist/assets/server/preference/preference.controller.js +66 -0
- package/dist/assets/server/preference/preference.controller.js.map +1 -0
- package/dist/assets/server/preference/preference.entity.d.ts +13 -0
- package/dist/assets/server/preference/preference.entity.js +77 -0
- package/dist/assets/server/preference/preference.entity.js.map +1 -0
- package/dist/assets/server/preference/preference.module.d.ts +2 -0
- package/dist/assets/server/preference/preference.module.js +26 -0
- package/dist/assets/server/preference/preference.module.js.map +1 -0
- package/dist/assets/server/preference/preference.service.d.ts +33 -0
- package/dist/assets/server/preference/preference.service.js +122 -0
- package/dist/assets/server/preference/preference.service.js.map +1 -0
- package/dist/assets/server/recording/recording-event-queue.service.js +1 -1
- package/dist/assets/server/recording/recording-event-queue.service.js.map +1 -1
- package/dist/assets/server/recording/recording-retention.service.d.ts +19 -2
- package/dist/assets/server/recording/recording-retention.service.js +95 -6
- package/dist/assets/server/recording/recording-retention.service.js.map +1 -1
- package/dist/assets/server/recording/recording-snapshot.service.d.ts +1 -0
- package/dist/assets/server/recording/recording-snapshot.service.js +4 -0
- package/dist/assets/server/recording/recording-snapshot.service.js.map +1 -1
- package/dist/assets/server/recording/recording.controller.d.ts +10 -0
- package/dist/assets/server/recording/recording.controller.js +19 -0
- package/dist/assets/server/recording/recording.controller.js.map +1 -1
- package/dist/assets/server/recording/recording.gateway.js +15 -0
- package/dist/assets/server/recording/recording.gateway.js.map +1 -1
- package/dist/assets/server/recording/recording.module.js +6 -1
- package/dist/assets/server/recording/recording.module.js.map +1 -1
- package/dist/assets/server/recording/recording.service.d.ts +5 -0
- package/dist/assets/server/recording/recording.service.js +10 -0
- package/dist/assets/server/recording/recording.service.js.map +1 -1
- package/dist/assets/server/remark/remark.controller.d.ts +1 -1
- package/dist/assets/server/remark/remark.controller.js +1 -10
- package/dist/assets/server/remark/remark.controller.js.map +1 -1
- package/dist/assets/server/remark/remark.entity.d.ts +1 -1
- package/dist/assets/server/remark/remark.entity.js.map +1 -1
- package/dist/assets/server/site/entities/site-registry.entity.d.ts +1 -1
- package/dist/assets/server/site/entities/site-registry.entity.js +1 -1
- package/dist/assets/server/site/entities/site-registry.entity.js.map +1 -1
- package/dist/assets/server/site/site.controller.d.ts +1 -1
- package/dist/assets/server/site/site.service.d.ts +5 -1
- package/dist/assets/server/site/site.service.js +13 -0
- package/dist/assets/server/site/site.service.js.map +1 -1
- package/dist/assets/server/system/system.controller.d.ts +1 -0
- package/dist/assets/server/system/system.controller.js +1 -1
- package/dist/assets/server/system/system.controller.js.map +1 -1
- package/dist/assets/server/system/system.service.d.ts +1 -0
- package/dist/assets/server/system/system.service.js +3 -2
- package/dist/assets/server/system/system.service.js.map +1 -1
- package/dist/cache/db.js +6 -6
- package/dist/commands/addons.js +2 -1
- package/dist/commands/cache.js +1 -1
- package/dist/commands/case.d.ts +67 -0
- package/dist/commands/case.js +101 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.js +8 -1
- package/dist/commands/customer.d.ts +25 -0
- package/dist/commands/customer.js +137 -0
- package/dist/commands/doctor.js +33 -9
- package/dist/commands/extension.js +21 -16
- package/dist/commands/extras.d.ts +18 -3
- package/dist/commands/extras.js +162 -5
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +3 -4
- package/dist/commands/install.d.ts +5 -0
- package/dist/commands/install.js +22 -42
- package/dist/commands/issue-env.d.ts +23 -0
- package/dist/commands/issue-env.js +101 -0
- package/dist/commands/job.d.ts +2 -1
- package/dist/commands/job.js +25 -5
- package/dist/commands/login.d.ts +4 -0
- package/dist/commands/login.js +57 -4
- package/dist/commands/migrate.d.ts +32 -0
- package/dist/commands/migrate.js +116 -0
- package/dist/commands/note.d.ts +3 -0
- package/dist/commands/note.js +75 -0
- package/dist/commands/ops.js +64 -13
- package/dist/commands/preference.d.ts +6 -0
- package/dist/commands/preference.js +80 -0
- package/dist/commands/record.js +1 -1
- package/dist/commands/report.js +1 -1
- package/dist/commands/search.js +1 -1
- package/dist/commands/server.d.ts +12 -0
- package/dist/commands/server.js +182 -18
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.js +16 -6
- package/dist/commands/update.js +7 -8
- package/dist/config.d.ts +21 -0
- package/dist/config.js +27 -0
- package/dist/extension-sync.d.ts +35 -0
- package/dist/extension-sync.js +159 -0
- package/dist/i18n/locales/en-US.json +17 -1
- package/dist/i18n/locales/zh-CN.json +17 -1
- package/dist/index.js +17 -0
- package/dist/keychain.d.ts +16 -1
- package/dist/keychain.js +43 -16
- package/dist/logger.js +2 -2
- package/dist/postinstall.d.ts +1 -0
- package/dist/postinstall.js +31 -0
- package/dist/updater.js +2 -2
- package/dist/utils/artifact-checksum.d.ts +4 -0
- package/dist/utils/artifact-checksum.js +40 -0
- package/dist/utils/command-error.d.ts +4 -2
- package/dist/utils/command-error.js +19 -3
- package/dist/utils/link.d.ts +18 -0
- package/dist/utils/link.js +83 -0
- package/dist/utils/local-http.d.ts +7 -0
- package/dist/utils/local-http.js +30 -0
- package/dist/version.js +86 -49
- package/dist/watchdog.d.ts +8 -0
- package/dist/watchdog.js +49 -9
- package/dist/web/dist/404.html +1 -1
- package/dist/web/dist/_next/static/chunks/app/logs/page-d478031d49f7ba00.js +1 -0
- package/dist/web/dist/_next/static/chunks/{main-app-80b038a312917d90.js → main-app-0af848156a5b13fc.js} +1 -1
- package/dist/web/dist/_next/static/chunks/pages/{_app-da7d628f49271ece.js → _app-3d844792ec91e4d1.js} +2 -2
- package/dist/web/dist/auth/verify.html +1 -1
- package/dist/web/dist/auth/verify.txt +1 -1
- package/dist/web/dist/companies.html +1 -1
- package/dist/web/dist/companies.txt +1 -1
- package/dist/web/dist/entity.html +1 -1
- package/dist/web/dist/entity.txt +1 -1
- package/dist/web/dist/index.html +1 -1
- package/dist/web/dist/index.txt +1 -1
- package/dist/web/dist/jobs.html +1 -1
- package/dist/web/dist/jobs.txt +1 -1
- package/dist/web/dist/logs.html +1 -1
- package/dist/web/dist/logs.txt +2 -2
- package/dist/web/dist/matching/jobs/placeholder/talents.html +1 -1
- package/dist/web/dist/matching/jobs/placeholder/talents.txt +1 -1
- package/dist/web/dist/matching/portraits/placeholder/jobs.html +1 -1
- package/dist/web/dist/matching/portraits/placeholder/jobs.txt +1 -1
- package/dist/web/dist/parselog.html +1 -1
- package/dist/web/dist/parselog.txt +1 -1
- package/dist/web/dist/recordings.html +1 -1
- package/dist/web/dist/recordings.txt +1 -1
- package/dist/web/dist/snapshots/placeholder/agent-prompt.html +1 -1
- package/dist/web/dist/snapshots/placeholder/agent-prompt.txt +1 -1
- package/dist/web/dist/talents.html +1 -1
- package/dist/web/dist/talents.txt +1 -1
- package/package.json +6 -4
- package/scripts/postinstall-run.mjs +14 -0
- package/dist/web/dist/_next/static/chunks/app/logs/page-9f41d44fa1add723.js +0 -1
- /package/dist/web/dist/_next/static/{hqJ-7vr4X71DNfzZugHJ- → 3bVkK4pAgbbmp5r0ThyBL}/_buildManifest.js +0 -0
- /package/dist/web/dist/_next/static/{hqJ-7vr4X71DNfzZugHJ- → 3bVkK4pAgbbmp5r0ThyBL}/_ssgManifest.js +0 -0
|
@@ -21,13 +21,15 @@ function http(baseUrl) {
|
|
|
21
21
|
headers: { 'Content-Type': 'application/json' },
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
-
/** 带鉴权的 client(server /extensions* 端点要求 UTOKEN,与 job/record/search 等模块一致)。
|
|
25
|
-
|
|
24
|
+
/** 带鉴权的 client(server /extensions* 端点要求 UTOKEN,与 job/record/search 等模块一致)。
|
|
25
|
+
* D4(R3 定案):timeoutMs 可传 —— axios 层默认 60s,server 侧 timeoutMs(-t)更大时
|
|
26
|
+
* HTTP 层先弃,linkedin search 等多页编排(分钟级)走 CLI 必死;+15s 缓冲覆盖 server 处理+回传。 */
|
|
27
|
+
async function httpAuth(baseUrl, timeoutMs) {
|
|
26
28
|
const { getToken } = await import('../keychain.js');
|
|
27
29
|
const token = await getToken();
|
|
28
30
|
return axios.create({
|
|
29
31
|
baseURL: baseUrl,
|
|
30
|
-
timeout: 60_000,
|
|
32
|
+
timeout: timeoutMs ?? 60_000,
|
|
31
33
|
headers: {
|
|
32
34
|
'Content-Type': 'application/json',
|
|
33
35
|
...(token ? { Authorization: `UTOKEN ${token}` } : {}),
|
|
@@ -37,7 +39,8 @@ async function httpAuth(baseUrl) {
|
|
|
37
39
|
function requireServerUrl() {
|
|
38
40
|
const config = loadConfig();
|
|
39
41
|
if (!config.serverUrl) {
|
|
40
|
-
|
|
42
|
+
// 同步上下文:手工构造的 Error 无 response.status,不会触发 issuer 诊断分支
|
|
43
|
+
void printCommandError('extension:no-server-url', new Error('请先 hunter-mate config 设置 serverUrl'));
|
|
41
44
|
process.exit(1);
|
|
42
45
|
}
|
|
43
46
|
return config.serverUrl;
|
|
@@ -126,7 +129,7 @@ export function ensureEnvReady() {
|
|
|
126
129
|
catch { /* ignore */ }
|
|
127
130
|
}
|
|
128
131
|
if (!serverUp) {
|
|
129
|
-
printCommandError('extension:server-start-failed', new Error('请手动执行 hunter-mate start -f'));
|
|
132
|
+
await printCommandError('extension:server-start-failed', new Error('请手动执行 hunter-mate start -f'));
|
|
130
133
|
process.exit(1);
|
|
131
134
|
}
|
|
132
135
|
// 2) 检查扩展是否安装
|
|
@@ -141,7 +144,7 @@ export function ensureEnvReady() {
|
|
|
141
144
|
const r = runInstall(init.extensionDir);
|
|
142
145
|
if (!r.source) {
|
|
143
146
|
// 内置 dist 不存在, 无法自动安装
|
|
144
|
-
printCommandError('extension:auto-install-failed', new Error('内置 extension dist 不存在,请在 hunter-mate monorepo 执行 pnpm --filter @hunter-mate/extension build,或手动 hunter-mate install'));
|
|
147
|
+
await printCommandError('extension:auto-install-failed', new Error('内置 extension dist 不存在,请在 hunter-mate monorepo 执行 pnpm --filter @hunter-mate/extension build,或手动 hunter-mate install'));
|
|
145
148
|
process.exit(1);
|
|
146
149
|
}
|
|
147
150
|
console.log(`${B.green}✓ 扩展已自动安装到 ${r.target}${B.reset}`);
|
|
@@ -183,7 +186,7 @@ export function ensureEnvReady() {
|
|
|
183
186
|
}
|
|
184
187
|
catch {
|
|
185
188
|
// /extensions 端点不可达
|
|
186
|
-
printCommandError('extension:check-failed', new Error('Server 可能未正确启动, 请执行 hunter-mate start -f'));
|
|
189
|
+
await printCommandError('extension:check-failed', new Error('Server 可能未正确启动, 请执行 hunter-mate start -f'));
|
|
187
190
|
process.exit(1);
|
|
188
191
|
}
|
|
189
192
|
})();
|
|
@@ -247,7 +250,7 @@ async function withEnvCheck(fn) {
|
|
|
247
250
|
await fn();
|
|
248
251
|
}
|
|
249
252
|
catch (err) {
|
|
250
|
-
printCommandError('extension:exec-failed', err);
|
|
253
|
+
await printCommandError('extension:exec-failed', err);
|
|
251
254
|
const msg = err.response?.data?.message ?? err.response?.data?.error ?? err.message;
|
|
252
255
|
if (msg.includes('no Hunter Mate Chrome extension registered') || msg.includes('no Hunter Mate')) {
|
|
253
256
|
const init = await import('./init.js').then((m) => m.runInit());
|
|
@@ -279,7 +282,7 @@ async function cmdControl(command, argsJson, opts) {
|
|
|
279
282
|
process.exit(1);
|
|
280
283
|
}
|
|
281
284
|
}
|
|
282
|
-
const r = await (await httpAuth(serverUrl)).post('/extensions/control', {
|
|
285
|
+
const r = await (await httpAuth(serverUrl, opts.timeout ? Number(opts.timeout) + 15_000 : undefined)).post('/extensions/control', {
|
|
283
286
|
command,
|
|
284
287
|
params,
|
|
285
288
|
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
@@ -292,9 +295,10 @@ async function cmdControl(command, argsJson, opts) {
|
|
|
292
295
|
async function cmdGoto(url, opts) {
|
|
293
296
|
return withEnvCheck(async () => {
|
|
294
297
|
const serverUrl = requireServerUrl();
|
|
295
|
-
const r = await (await httpAuth(serverUrl)).post('/extensions/goto', {
|
|
298
|
+
const r = await (await httpAuth(serverUrl, opts.timeout ? Number(opts.timeout) + 15_000 : undefined)).post('/extensions/goto', {
|
|
296
299
|
url,
|
|
297
300
|
waitUntil: opts.waitUntil,
|
|
301
|
+
reload: opts.reload ? true : undefined,
|
|
298
302
|
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
299
303
|
extensionId: opts.extensionId,
|
|
300
304
|
sessionId: opts.sessionId ?? opts.taskId,
|
|
@@ -305,7 +309,7 @@ async function cmdGoto(url, opts) {
|
|
|
305
309
|
async function cmdEval(expression, opts) {
|
|
306
310
|
return withEnvCheck(async () => {
|
|
307
311
|
const serverUrl = requireServerUrl();
|
|
308
|
-
const r = await (await httpAuth(serverUrl)).post('/extensions/eval', {
|
|
312
|
+
const r = await (await httpAuth(serverUrl, opts.timeout ? Number(opts.timeout) + 15_000 : undefined)).post('/extensions/eval', {
|
|
309
313
|
expression,
|
|
310
314
|
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
311
315
|
extensionId: opts.extensionId,
|
|
@@ -317,7 +321,7 @@ async function cmdEval(expression, opts) {
|
|
|
317
321
|
async function cmdScreenshot(opts) {
|
|
318
322
|
return withEnvCheck(async () => {
|
|
319
323
|
const serverUrl = requireServerUrl();
|
|
320
|
-
const r = await (await httpAuth(serverUrl)).post('/extensions/screenshot', {
|
|
324
|
+
const r = await (await httpAuth(serverUrl, opts.timeout ? Number(opts.timeout) + 15_000 : undefined)).post('/extensions/screenshot', {
|
|
321
325
|
fullPage: opts.full ? true : undefined,
|
|
322
326
|
selector: opts.selector,
|
|
323
327
|
format: opts.imageFormat ?? 'png',
|
|
@@ -354,7 +358,7 @@ async function cmdRun(site, command, opts) {
|
|
|
354
358
|
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
355
359
|
extensionId: opts.extensionId,
|
|
356
360
|
};
|
|
357
|
-
const r = await (await httpAuth(serverUrl)).post(endpoint, payload);
|
|
361
|
+
const r = await (await httpAuth(serverUrl, opts.timeout ? Number(opts.timeout) + 15_000 : undefined)).post(endpoint, payload);
|
|
358
362
|
printResult(r.data, opts.format);
|
|
359
363
|
});
|
|
360
364
|
}
|
|
@@ -382,7 +386,7 @@ extensionCommand
|
|
|
382
386
|
console.log(`\n${B.green}✓ Hunter Mate 扩展已连接 (${count} 个浏览器实例)${B.reset}\n`);
|
|
383
387
|
}
|
|
384
388
|
catch (err) {
|
|
385
|
-
printCommandError('extension:check-error', err);
|
|
389
|
+
await printCommandError('extension:check-error', err);
|
|
386
390
|
process.exit(1);
|
|
387
391
|
}
|
|
388
392
|
});
|
|
@@ -394,8 +398,9 @@ common(extensionCommand
|
|
|
394
398
|
.description('通用页面控制(OpenCLI 全能力, 对应 chromepilot control):cp.state/cp.goto/cp.find/cp.click/cp.type/cp.fill/cp.select/cp.keys/cp.hover/cp.focus/cp.dblclick/cp.check/cp.uncheck/cp.get/cp.extract/cp.eval/cp.network/cp.frames/cp.wait/cp.upload/cp.drag/cp.scroll/cp.back/cp.screenshot/cp.insert/cp.cdp/cp.cookies/cp.window.close/cp.tab.*(list|new|select|close|bind|unbind)。参数 JSON 内 "__tab":"<tabId>" 定向 tab')).action(async (command, argsJson, opts) => cmdControl(command, argsJson, opts));
|
|
395
399
|
common(extensionCommand
|
|
396
400
|
.command('goto <url>')
|
|
397
|
-
.description('导航到指定 URL (对应 chromepilot control cp.goto)'))
|
|
401
|
+
.description('导航到指定 URL (对应 chromepilot control cp.goto;同 URL 默认复用返回 reused:true,--reload 强制刷新)'))
|
|
398
402
|
.option('--wait-until <mode>', 'domcontentloaded|load|networkidle0|networkidle2')
|
|
403
|
+
.option('--reload', '同 URL 时也强制 location.reload()(FID-077 复用+强制重载模式)')
|
|
399
404
|
.action(async (url, opts) => cmdGoto(url, opts));
|
|
400
405
|
common(extensionCommand
|
|
401
406
|
.command('eval <expression>')
|
|
@@ -410,6 +415,6 @@ common(extensionCommand
|
|
|
410
415
|
.action(async (opts) => cmdScreenshot(opts));
|
|
411
416
|
common(extensionCommand
|
|
412
417
|
.command('run <site> <command>')
|
|
413
|
-
.description('站点适配器命令 (对应 chromepilot run): linkedin search|extract|invite 或 resume extract'))
|
|
418
|
+
.description('站点适配器命令 (对应 chromepilot run): linkedin search|extract|invite、liepin search|extract 或 resume extract'))
|
|
414
419
|
.option('--args <json>', '命令参数 JSON 字符串')
|
|
415
420
|
.action(async (site, command, opts) => cmdRun(site, command, opts));
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
/** 目录最新 mtime(recursive);不可读返回 0。供前视守卫与静止等待共用 */
|
|
3
|
+
export declare function latestMtimeMs(dir: string): number;
|
|
4
|
+
export declare function waitForExtensionDirQuiet(dir: string, opts?: {
|
|
5
|
+
quietMs?: number;
|
|
6
|
+
maxWaitMs?: number;
|
|
7
|
+
pollMs?: number;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
quiet: boolean;
|
|
10
|
+
waitedMs: number;
|
|
11
|
+
newestMtimeMs: number;
|
|
12
|
+
}>;
|
|
13
|
+
/** FID-092:轮询 server 已连接扩展数(>=1 即判定 reload 后已回连)。 */
|
|
14
|
+
export declare function extensionConnectedCount(serverUrl: string, timeoutMs?: number): Promise<number>;
|
|
2
15
|
/**
|
|
3
|
-
* 通知所有已连接扩展 reload
|
|
4
|
-
* 1)
|
|
5
|
-
* 2)
|
|
16
|
+
* 通知所有已连接扩展 reload(自愈主链路),FID-092 三段式:
|
|
17
|
+
* 1) dist 静止确认(防 reload 读到写一半的扩展文件)
|
|
18
|
+
* 2) 广播(broadcastReload):优先 HTTP POST /system/broadcast-extension;
|
|
19
|
+
* 旧 server 无该端点时退回直连 WS /ws 广播
|
|
20
|
+
* 3) 健康校验 + 自动重试(广播后 12s 内未回连 → 再广播,最多 3 次)
|
|
6
21
|
*/
|
|
7
22
|
export declare function runReload(timeoutMs?: number): Promise<{
|
|
8
23
|
broadcast: number;
|
package/dist/commands/extras.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
3
|
import * as readline from 'readline';
|
|
4
|
-
import { loadConfig } from '../config.js';
|
|
4
|
+
import { loadConfig, resolveDataDir } from '../config.js';
|
|
5
5
|
import axios from 'axios';
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
@@ -10,12 +10,162 @@ import { runStop, tryReadPid } from './server.js';
|
|
|
10
10
|
import { i18n } from '../i18n/index.js';
|
|
11
11
|
import { getCliLogger } from '../logger.js';
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* FID-092:扫描扩展目录最新 mtime,等待其静止——reload 广播前确认 dist 已写完。
|
|
14
|
+
* 背景:R5 实机 reload 后 3 秒 dist 仍在写入,Chrome 重读读到写了一半的
|
|
15
|
+
* service-worker.js → SW eval 崩溃 → 无 alarm 监听/无 offscreen/零重连,
|
|
16
|
+
* 所有自愈路径全部失效,只能人工点。Windows 兼容:纯 node fs,无 shell。
|
|
17
|
+
*/
|
|
18
|
+
const newestMtime = (d) => {
|
|
19
|
+
let latest = 0;
|
|
20
|
+
let entries;
|
|
21
|
+
try {
|
|
22
|
+
entries = fs.readdirSync(d, { withFileTypes: true });
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return latest; // 目录不可读(未安装/权限)时不阻塞 reload
|
|
26
|
+
}
|
|
27
|
+
for (const e of entries) {
|
|
28
|
+
const full = path.join(d, e.name);
|
|
29
|
+
try {
|
|
30
|
+
if (e.isDirectory()) {
|
|
31
|
+
latest = Math.max(latest, newestMtime(full));
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
latest = Math.max(latest, fs.statSync(full).mtimeMs);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
/* 单个文件竞态删除,跳过 */
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return latest;
|
|
42
|
+
};
|
|
43
|
+
/** 目录最新 mtime(recursive);不可读返回 0。供前视守卫与静止等待共用 */
|
|
44
|
+
export function latestMtimeMs(dir) {
|
|
45
|
+
return newestMtime(dir);
|
|
46
|
+
}
|
|
47
|
+
export async function waitForExtensionDirQuiet(dir, opts = {}) {
|
|
48
|
+
const quietMs = opts.quietMs ?? 2500;
|
|
49
|
+
const maxWaitMs = opts.maxWaitMs ?? 15000;
|
|
50
|
+
const pollMs = opts.pollMs ?? 400;
|
|
51
|
+
const start = Date.now();
|
|
52
|
+
let newest = newestMtime(dir);
|
|
53
|
+
while (newest > 0 && Date.now() - newest < quietMs && Date.now() - start < maxWaitMs) {
|
|
54
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
55
|
+
newest = newestMtime(dir);
|
|
56
|
+
}
|
|
57
|
+
return { quiet: newest === 0 || Date.now() - newest >= quietMs, waitedMs: Date.now() - start, newestMtimeMs: newest };
|
|
58
|
+
}
|
|
59
|
+
/** FID-092:轮询 server 已连接扩展数(>=1 即判定 reload 后已回连)。 */
|
|
60
|
+
export async function extensionConnectedCount(serverUrl, timeoutMs = 3000) {
|
|
61
|
+
try {
|
|
62
|
+
const r = await axios.get(`${serverUrl}/extensions`, { timeout: timeoutMs, proxy: false });
|
|
63
|
+
return Array.isArray(r.data) ? r.data.length : 0;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return -1; // server 不可达
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 通知所有已连接扩展 reload(自愈主链路),FID-092 三段式:
|
|
71
|
+
* 1) dist 静止确认(防 reload 读到写一半的扩展文件)
|
|
72
|
+
* 2) 广播(broadcastReload):优先 HTTP POST /system/broadcast-extension;
|
|
73
|
+
* 旧 server 无该端点时退回直连 WS /ws 广播
|
|
74
|
+
* 3) 健康校验 + 自动重试(广播后 12s 内未回连 → 再广播,最多 3 次)
|
|
16
75
|
*/
|
|
17
76
|
export async function runReload(timeoutMs = 5000) {
|
|
18
77
|
const config = loadConfig();
|
|
78
|
+
// FID-092 步骤 1:dist 静止确认(软链/目录均可,扫描真实路径)
|
|
79
|
+
let extDir;
|
|
80
|
+
try {
|
|
81
|
+
extDir = fs.realpathSync(path.join(resolveDataDir(), 'extension'));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
extDir = path.join(resolveDataDir(), 'extension');
|
|
85
|
+
}
|
|
86
|
+
const VERIFY_WINDOW_MS = 12000;
|
|
87
|
+
const MAX_ATTEMPTS = 3;
|
|
88
|
+
// FID-093 前视守卫:dist 静止确认只看"过去",构建中(tsc 编译 shared 等
|
|
89
|
+
// 数分钟不碰 extension dist)会误判静止 → 广播后构建 resumed 写 dist →
|
|
90
|
+
// 重载的扩展读到半写完文件 → SW eval 崩溃死透(2026-09-10 实机事故)。
|
|
91
|
+
// 对策:回连后监视 dist 一个窗口期,发现仍在写入 → 判本次 reload 中毒,
|
|
92
|
+
// 等静止后重播(有界)。
|
|
93
|
+
const POISON_WINDOW_MS = 10_000;
|
|
94
|
+
const POISON_POLL_MS = 500;
|
|
95
|
+
const MAX_POISON_REBROADCAST = 2;
|
|
96
|
+
const quietNotes = [];
|
|
97
|
+
let last = { broadcast: 0, msg: '' };
|
|
98
|
+
let poisonRebroadcasts = 0;
|
|
99
|
+
while (true) {
|
|
100
|
+
const q = await waitForExtensionDirQuiet(extDir);
|
|
101
|
+
if (q.waitedMs > 500)
|
|
102
|
+
quietNotes.push(`(等待 dist 写入静止 ${q.waitedMs}ms)`);
|
|
103
|
+
const m0 = latestMtimeMs(extDir);
|
|
104
|
+
// FID-092 步骤 2+3:广播 → 校验回连 → 失败重试
|
|
105
|
+
last = await broadcastReload(config, timeoutMs);
|
|
106
|
+
if (last.broadcast <= 0) {
|
|
107
|
+
return { ...last, msg: `${quietNotes.join(' ')}${last.msg}` };
|
|
108
|
+
}
|
|
109
|
+
const start = Date.now();
|
|
110
|
+
let connected = 0;
|
|
111
|
+
while (Date.now() - start < VERIFY_WINDOW_MS) {
|
|
112
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
113
|
+
const n = await extensionConnectedCount(config.serverUrl);
|
|
114
|
+
if (n > 0) {
|
|
115
|
+
connected = n;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
if (n === -1)
|
|
119
|
+
break; // server 挂了,重试无意义
|
|
120
|
+
}
|
|
121
|
+
if (connected <= 0)
|
|
122
|
+
break; // 走下方未回连重试路径
|
|
123
|
+
// 回连成功 → 前视窗口监视 dist 是否被构建 resumed 写入
|
|
124
|
+
const poisonDeadline = Date.now() + POISON_WINDOW_MS;
|
|
125
|
+
let poisoned = false;
|
|
126
|
+
while (Date.now() < poisonDeadline) {
|
|
127
|
+
await new Promise((r) => setTimeout(r, POISON_POLL_MS));
|
|
128
|
+
if (latestMtimeMs(extDir) > m0) {
|
|
129
|
+
poisoned = true;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!poisoned) {
|
|
134
|
+
return {
|
|
135
|
+
...last,
|
|
136
|
+
msg: `${quietNotes.join(' ')}${last.msg};已校验扩展回连(connected=${connected})。`,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (poisonRebroadcasts >= MAX_POISON_REBROADCAST) {
|
|
140
|
+
return {
|
|
141
|
+
...last,
|
|
142
|
+
msg: `${quietNotes.join(' ')}${last.msg};已回连,但 dist 在回连后仍持续被写入(构建进行中),` +
|
|
143
|
+
`重播 ${MAX_POISON_REBROADCAST} 次后仍在写——reload 可能再次读到半写完文件,请等构建结束后再执行 hunter-mate reload。`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
poisonRebroadcasts++;
|
|
147
|
+
quietNotes.push(`(回连后发现 dist 仍在写入:构建 resumed,等静止后第 ${poisonRebroadcasts} 次重播)`);
|
|
148
|
+
}
|
|
149
|
+
// 未回连:重试广播(第一次读到的可能是写了一半的文件,重读即恢复)
|
|
150
|
+
for (let attempt = 2; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
151
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
152
|
+
last = await broadcastReload(config, timeoutMs);
|
|
153
|
+
if (last.broadcast <= 0)
|
|
154
|
+
break;
|
|
155
|
+
const deadline = Date.now() + VERIFY_WINDOW_MS;
|
|
156
|
+
while (Date.now() < deadline) {
|
|
157
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
158
|
+
if ((await extensionConnectedCount(config.serverUrl)) > 0) {
|
|
159
|
+
return { ...last, msg: `${quietNotes.join(' ')}第 ${attempt} 次广播后已校验扩展回连。` };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
...last,
|
|
165
|
+
msg: `${quietNotes.join(' ')}${MAX_ATTEMPTS} 次广播后扩展仍未回连(server /extensions=0)——扩展可能处于错误态,请到 chrome://extensions 查看 HunterMate「错误」并点「重新加载」。`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
async function broadcastReload(config, timeoutMs = 5000) {
|
|
19
169
|
// ---------- 1) HTTP 优先(POST /system/broadcast-extension) ----------
|
|
20
170
|
try {
|
|
21
171
|
const res = await axios.post(`${config.serverUrl}/system/broadcast-extension`, { payload: { type: 'reload' } }, { timeout: timeoutMs, proxy: false });
|
|
@@ -29,6 +179,13 @@ export async function runReload(timeoutMs = 5000) {
|
|
|
29
179
|
msg: `reload 广播已通过 HTTP 通道发送给 ${broadcast} 个扩展实例:200ms 后每个实例会自动调 chrome.runtime.reload(),Chrome 将重新读取磁盘上的扩展文件。`,
|
|
30
180
|
};
|
|
31
181
|
}
|
|
182
|
+
// FID-086 deferred reload: server 已挂起意图,扩展重连(≤30s)自动下发——断连态不失败、不静默
|
|
183
|
+
if (body.queued) {
|
|
184
|
+
return {
|
|
185
|
+
broadcast: 0,
|
|
186
|
+
msg: '扩展当前未连接,重载请求已挂起:扩展重连(通常 ≤30 秒,chrome.alarms 唤醒)后将自动重新加载。无需手动干预。',
|
|
187
|
+
};
|
|
188
|
+
}
|
|
32
189
|
if (errors.length) {
|
|
33
190
|
return {
|
|
34
191
|
broadcast: 0,
|
|
@@ -143,7 +300,7 @@ export const openExtCommand = new Command('open-extension-folder')
|
|
|
143
300
|
// ---------------------------------------------------------------------------
|
|
144
301
|
// purge — 彻底清理本地 hunter-mate 数据
|
|
145
302
|
// ---------------------------------------------------------------------------
|
|
146
|
-
const HM_ROOT =
|
|
303
|
+
const HM_ROOT = resolveDataDir;
|
|
147
304
|
/** 交互式二次确认 */
|
|
148
305
|
function confirmPrompt(question) {
|
|
149
306
|
return new Promise((resolve) => {
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
/** 对应 ChromePilot init:
|
|
2
|
+
/** 对应 ChromePilot init: 创建数据目录、SQLite、初始 token(目录位置随 dataDir 配置,FID-071) */
|
|
3
3
|
export declare function runInit(force?: boolean): {
|
|
4
4
|
dir: string;
|
|
5
5
|
extensionDir: string;
|
package/dist/commands/init.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import { ensureConfigDir, loadConfig, saveConfig } from '../config.js';
|
|
4
|
+
import { ensureConfigDir, loadConfig, saveConfig, resolveDataDir } from '../config.js';
|
|
5
5
|
import { getCliLogger } from '../logger.js';
|
|
6
|
-
/** 对应 ChromePilot init:
|
|
6
|
+
/** 对应 ChromePilot init: 创建数据目录、SQLite、初始 token(目录位置随 dataDir 配置,FID-071) */
|
|
7
7
|
export function runInit(force = false) {
|
|
8
|
-
const
|
|
9
|
-
const ROOT = path.join(HOME, '.hunter-mate');
|
|
8
|
+
const ROOT = resolveDataDir();
|
|
10
9
|
const EXT_DIR = path.join(ROOT, 'extension');
|
|
11
10
|
const DB_FILE = path.join(ROOT, 'hunter-mate.db');
|
|
12
11
|
const SESSIONS_DIR = path.join(ROOT, 'sessions');
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
/** 对应 ChromePilot install: 把内置 extension dist 同步到 ~/.hunter-mate/extension
|
|
3
|
+
* (Issue #23 起走 syncExtensionArtifacts:checksum 比对 + 剪枝,不再只增不剪) */
|
|
2
4
|
export declare function runInstall(target?: string): {
|
|
3
5
|
source: string | null;
|
|
4
6
|
target: string;
|
|
5
7
|
copied: number;
|
|
8
|
+
removed: number;
|
|
9
|
+
skipped: boolean;
|
|
10
|
+
reason?: string;
|
|
6
11
|
};
|
|
7
12
|
/** 友好引导: 告诉用户如何在 Chrome 中加载 Hunter Mate 扩展 */
|
|
8
13
|
export declare function showInstallGuide(extDir: string): void;
|
package/dist/commands/install.js
CHANGED
|
@@ -1,50 +1,22 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
2
|
import * as path from 'path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
3
|
import { runInit } from './init.js';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
function resolveBuiltinExtensionDist() {
|
|
10
|
-
// 优先 bundled-assets(npm 安装模式:dist/assets/extension 已存在)
|
|
11
|
-
const bundled = findBundledAssets();
|
|
12
|
-
if (bundled)
|
|
13
|
-
return bundled.extensionDir;
|
|
14
|
-
// fallback:monorepo 并列路径(老 hunter-mate 开发模式)
|
|
15
|
-
const candidates = [
|
|
16
|
-
path.resolve(__dirname, '../../../extension/dist'), // monorepo: packages/cli/dist/commands -> packages/extension/dist
|
|
17
|
-
path.resolve(__dirname, '../../bundled-extension'), // bundled
|
|
18
|
-
];
|
|
19
|
-
for (const c of candidates)
|
|
20
|
-
if (fs.existsSync(c))
|
|
21
|
-
return c;
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
4
|
+
import { compareExtensionArtifacts, syncExtensionArtifacts } from '../extension-sync.js';
|
|
5
|
+
/** 对应 ChromePilot install: 把内置 extension dist 同步到 ~/.hunter-mate/extension
|
|
6
|
+
* (Issue #23 起走 syncExtensionArtifacts:checksum 比对 + 剪枝,不再只增不剪) */
|
|
24
7
|
export function runInstall(target) {
|
|
25
8
|
const init = runInit();
|
|
26
9
|
const targetDir = target ?? init.extensionDir;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
for (const f of fs.readdirSync(src))
|
|
38
|
-
copy(path.join(src, f), path.join(dst, f));
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
fs.copyFileSync(src, dst);
|
|
42
|
-
copied++;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
copy(source, targetDir);
|
|
46
|
-
}
|
|
47
|
-
return { source, target: targetDir, copied };
|
|
10
|
+
const cmp = compareExtensionArtifacts(targetDir);
|
|
11
|
+
const r = syncExtensionArtifacts({ target: targetDir, ensureTarget: true });
|
|
12
|
+
return {
|
|
13
|
+
source: cmp.source,
|
|
14
|
+
target: targetDir,
|
|
15
|
+
copied: r.copied,
|
|
16
|
+
removed: r.removed,
|
|
17
|
+
skipped: !r.synced,
|
|
18
|
+
reason: r.reason,
|
|
19
|
+
};
|
|
48
20
|
}
|
|
49
21
|
const B = {
|
|
50
22
|
reset: '\x1b[0m', bold: '\x1b[1m',
|
|
@@ -84,7 +56,15 @@ export const installCommand = new Command('install')
|
|
|
84
56
|
` 目标目录已准备好: ${B.cyan}${r.target}${B.reset}`);
|
|
85
57
|
return;
|
|
86
58
|
}
|
|
87
|
-
|
|
59
|
+
if (r.skipped && r.reason === 'identical') {
|
|
60
|
+
console.log(`${B.green}✓ 扩展产物已是最新,无需同步${B.reset} → ${B.cyan}${r.target}${B.reset}`);
|
|
61
|
+
}
|
|
62
|
+
else if (r.skipped) {
|
|
63
|
+
console.log(`${B.yellow}⚠ 扩展产物同步跳过: ${r.reason}${B.reset} → ${B.cyan}${r.target}${B.reset}`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log(`${B.green}✓ hunter-mate install OK: copied=${r.copied}, removed=${r.removed}${B.reset} → ${B.cyan}${r.target}${B.reset}`);
|
|
67
|
+
}
|
|
88
68
|
// 自动打开扩展所在目录(父目录),方便用户把 extension 文件夹拖到 chrome://extensions
|
|
89
69
|
if (opts.open !== false) {
|
|
90
70
|
try {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export interface IssueEnvGitInfo {
|
|
3
|
+
repoRoot: string;
|
|
4
|
+
head: string | null;
|
|
5
|
+
branch: string | null;
|
|
6
|
+
/** 最近一次提交(短 hash + 时间 + subject),与 issue 模板「代码 HEAD」行对应 */
|
|
7
|
+
lastCommit: string | null;
|
|
8
|
+
/** 有未提交改动 = dist 可能滞后于 HEAD,提 issue 必须注明 */
|
|
9
|
+
dirty: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface IssueEnvResult {
|
|
12
|
+
cliVersion: string;
|
|
13
|
+
/** dev = CLI 位于 git 工作区(软链/shim 到源码仓);package = 发布包安装 */
|
|
14
|
+
installType: 'dev' | 'package';
|
|
15
|
+
git: IssueEnvGitInfo | null;
|
|
16
|
+
/** server 未运行时为 null */
|
|
17
|
+
serverBuildTime: string | null;
|
|
18
|
+
os: string;
|
|
19
|
+
node: string;
|
|
20
|
+
dataDir: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function runIssueEnv(): Promise<IssueEnvResult>;
|
|
23
|
+
export declare const issueEnvCommand: Command;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { execFileSync } from 'node:child_process';
|
|
7
|
+
import axios from 'axios';
|
|
8
|
+
import { i18n } from '../i18n/index.js';
|
|
9
|
+
import { loadConfig, resolveDataDir } from '../config.js';
|
|
10
|
+
import { readInstalledVersions } from '../version.js';
|
|
11
|
+
// dev 软链/shim 安装时 __dirname 位于仓内(packages/cli/dist/commands),
|
|
12
|
+
// 发布包安装时位于全局 node_modules —— 向上找含 .git 的目录即可判定并定位仓根。
|
|
13
|
+
const START_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
function findRepoRoot(start) {
|
|
15
|
+
let dir = start;
|
|
16
|
+
while (true) {
|
|
17
|
+
if (fs.existsSync(path.join(dir, '.git')))
|
|
18
|
+
return dir;
|
|
19
|
+
const parent = path.dirname(dir);
|
|
20
|
+
if (parent === dir)
|
|
21
|
+
return null;
|
|
22
|
+
dir = parent;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** 静默执行 git,失败(git 不可用/非工作区)返回 null */
|
|
26
|
+
function git(args, cwd) {
|
|
27
|
+
try {
|
|
28
|
+
const out = execFileSync('git', args, {
|
|
29
|
+
cwd,
|
|
30
|
+
encoding: 'utf-8',
|
|
31
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
32
|
+
}).trim();
|
|
33
|
+
return out.length > 0 ? out : null;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function runIssueEnv() {
|
|
40
|
+
const installed = readInstalledVersions();
|
|
41
|
+
const repoRoot = findRepoRoot(START_DIR);
|
|
42
|
+
let gitInfo = null;
|
|
43
|
+
if (repoRoot) {
|
|
44
|
+
const porcelain = git(['status', '--porcelain'], repoRoot) ?? '';
|
|
45
|
+
gitInfo = {
|
|
46
|
+
repoRoot,
|
|
47
|
+
head: git(['rev-parse', '--short', 'HEAD'], repoRoot),
|
|
48
|
+
branch: git(['branch', '--show-current'], repoRoot),
|
|
49
|
+
lastCommit: git(['log', '-1', '--format=%h %ci %s'], repoRoot),
|
|
50
|
+
dirty: porcelain.length > 0,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
let serverBuildTime = null;
|
|
54
|
+
try {
|
|
55
|
+
const r = await axios.get(`${loadConfig().serverUrl}/health`, { timeout: 2000 });
|
|
56
|
+
serverBuildTime = r.data?.buildTime ?? null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
/* server 未运行,保持 null */
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
cliVersion: installed.cli ?? 'unknown',
|
|
63
|
+
installType: repoRoot ? 'dev' : 'package',
|
|
64
|
+
git: gitInfo,
|
|
65
|
+
serverBuildTime,
|
|
66
|
+
os: `${os.platform()} ${os.release()} ${os.arch()}`,
|
|
67
|
+
node: process.version,
|
|
68
|
+
dataDir: resolveDataDir(),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function renderText(r) {
|
|
72
|
+
const lines = [];
|
|
73
|
+
lines.push(`${i18n.t('issueEnv.cliVersion')}: ${r.cliVersion}`);
|
|
74
|
+
lines.push(`${i18n.t('issueEnv.installType')}: ` +
|
|
75
|
+
(r.installType === 'dev' ? i18n.t('issueEnv.installDev') : i18n.t('issueEnv.installPackage')));
|
|
76
|
+
if (r.git) {
|
|
77
|
+
lines.push(`${i18n.t('issueEnv.gitHead')}: ${r.git.lastCommit ?? r.git.head ?? 'unknown'}`);
|
|
78
|
+
lines.push(`${i18n.t('issueEnv.gitBranch')}: ${r.git.branch ?? 'unknown'} · ` +
|
|
79
|
+
(r.git.dirty ? i18n.t('issueEnv.gitDirty') : i18n.t('issueEnv.gitClean')));
|
|
80
|
+
lines.push(`${i18n.t('issueEnv.repoRoot')}: ${r.git.repoRoot}`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
lines.push(`${i18n.t('issueEnv.gitHead')}: ${i18n.t('issueEnv.gitUnavailable')}`);
|
|
84
|
+
}
|
|
85
|
+
lines.push(`${i18n.t('issueEnv.serverBuildTime')}: ${r.serverBuildTime ?? i18n.t('issueEnv.serverNotRunning')}`);
|
|
86
|
+
lines.push(`OS: ${r.os}`);
|
|
87
|
+
lines.push(`Node: ${r.node}`);
|
|
88
|
+
lines.push(`dataDir: ${r.dataDir}`);
|
|
89
|
+
return lines.join('\n');
|
|
90
|
+
}
|
|
91
|
+
export const issueEnvCommand = new Command('issue-env')
|
|
92
|
+
.description(i18n.t('issueEnv.description'))
|
|
93
|
+
.option('-f, --format <fmt>', '输出格式: text|json', 'text')
|
|
94
|
+
.action(async (opts) => {
|
|
95
|
+
const result = await runIssueEnv();
|
|
96
|
+
if (opts.format === 'json') {
|
|
97
|
+
console.log(JSON.stringify(result, null, 2));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
console.log(renderText(result));
|
|
101
|
+
});
|
package/dist/commands/job.d.ts
CHANGED
|
@@ -3,8 +3,9 @@ export interface CreateJobCaseResult {
|
|
|
3
3
|
id: number;
|
|
4
4
|
externalId: string;
|
|
5
5
|
status: string;
|
|
6
|
+
customerId?: number;
|
|
6
7
|
}
|
|
7
|
-
export declare function runJobCreate(folderPath: string): Promise<CreateJobCaseResult>;
|
|
8
|
+
export declare function runJobCreate(folderPath: string, customerRef?: string): Promise<CreateJobCaseResult>;
|
|
8
9
|
/**
|
|
9
10
|
* 优先查 server 的 /job-cases;server 不可达或未登录时兜底本地 sqlite 缓存。
|
|
10
11
|
*/
|