hunter-mate 0.1.7 → 0.1.8
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 +727 -152
- package/dist/assets/extension/build-info.json +1 -0
- package/dist/assets/extension/content/index.js +22 -4
- 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 +12 -0
- package/dist/assets/server/extension/extension-control.controller.js +41 -1
- 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/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 +59 -4
- 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 +4 -0
- 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.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 +20 -0
- package/dist/assets/server/governance/governance.ingester.js +114 -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 +26 -0
- package/dist/assets/server/governance/governance.module.js.map +1 -0
- package/dist/assets/server/governance/governance.service.d.ts +114 -0
- package/dist/assets/server/governance/governance.service.js +356 -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 +11 -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 +18 -15
- package/dist/commands/extras.d.ts +22 -3
- package/dist/commands/extras.js +114 -5
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +3 -4
- 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 +30 -3
- 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 +7 -0
- package/dist/commands/server.js +40 -2
- 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/i18n/locales/en-US.json +17 -1
- package/dist/i18n/locales/zh-CN.json +17 -1
- package/dist/index.js +14 -0
- package/dist/keychain.d.ts +16 -1
- package/dist/keychain.js +43 -16
- package/dist/logger.js +2 -2
- package/dist/updater.js +2 -2
- 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/version.js +33 -8
- package/dist/watchdog.js +3 -3
- 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/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 +2 -2
- package/dist/web/dist/_next/static/chunks/app/logs/page-9f41d44fa1add723.js +0 -1
- /package/dist/web/dist/_next/static/{hqJ-7vr4X71DNfzZugHJ- → WdJr3NhHjlu5xN0qOap1k}/_buildManifest.js +0 -0
- /package/dist/web/dist/_next/static/{hqJ-7vr4X71DNfzZugHJ- → WdJr3NhHjlu5xN0qOap1k}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { loadConfig } from '../config.js';
|
|
4
|
+
import { getToken } from '../keychain.js';
|
|
5
|
+
import { printCommandError } from '../utils/command-error.js';
|
|
6
|
+
import { resolveCustomer } from './customer.js';
|
|
7
|
+
async function authedHeaders() {
|
|
8
|
+
const token = await getToken().catch(() => undefined);
|
|
9
|
+
return token ? { Authorization: `UTOKEN ${token}` } : {};
|
|
10
|
+
}
|
|
11
|
+
async function getMergedPreferences(scope, scopeId, customerId) {
|
|
12
|
+
const config = loadConfig();
|
|
13
|
+
const res = await axios.get(`${config.serverUrl}/preferences`, {
|
|
14
|
+
params: { scope, scopeId, ...(customerId ? { customerId } : {}) },
|
|
15
|
+
headers: await authedHeaders(),
|
|
16
|
+
proxy: false,
|
|
17
|
+
});
|
|
18
|
+
return res.data;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 记忆完整性探针:推荐前自检用。
|
|
22
|
+
* 输出 customer / positions / memory(备注+不合适原因) / preferences(三层) / missing(缺口清单,空=完整)。
|
|
23
|
+
*/
|
|
24
|
+
export async function runCaseCheck(customerRef, jobExternalId) {
|
|
25
|
+
const related = await resolveCustomer(customerRef);
|
|
26
|
+
if (!related) {
|
|
27
|
+
return {
|
|
28
|
+
customer: { known: false, ref: customerRef },
|
|
29
|
+
positions: [],
|
|
30
|
+
memory: { remarksCount: 0, lastRemarkAt: null, rejectedReasons: [] },
|
|
31
|
+
preferences: { user: [], customer: [], case: [] },
|
|
32
|
+
missing: [`客户 "${customerRef}" 未入库 — 先运行: hunter-mate customer upsert --name "${customerRef}"`],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const { customer, jobCases, remarks } = related;
|
|
36
|
+
let jobCase = null;
|
|
37
|
+
if (jobExternalId) {
|
|
38
|
+
jobCase = jobCases.find((j) => j.externalId === jobExternalId || String(j.id) === String(jobExternalId)) ?? null;
|
|
39
|
+
}
|
|
40
|
+
else if (jobCases.length) {
|
|
41
|
+
// 未指定 --job 时取最新职位 case 做三层检查;否则 scope 退化为 customer,
|
|
42
|
+
// case 层永远空数组、case 层缺口检测失效(RS 模拟 R2 抓出)
|
|
43
|
+
jobCase = [...jobCases].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())[0];
|
|
44
|
+
}
|
|
45
|
+
const prefs = await getMergedPreferences(jobCase ? 'case' : 'customer', jobCase ? jobCase.id : customer.id, customer.id).catch(() => ({ case: [], customer: [], user: [] }));
|
|
46
|
+
const feedbackRemarks = remarks.filter((r) => r.category === 'feedback');
|
|
47
|
+
const rejectedReasons = [
|
|
48
|
+
...feedbackRemarks
|
|
49
|
+
.map((r) => ({ source: 'remark', content: String(r.content).slice(0, 200), at: r.createdAt })),
|
|
50
|
+
...prefs.case
|
|
51
|
+
.filter((p) => p.dimension === 'reject_reason')
|
|
52
|
+
.map((p) => ({ source: 'preference', content: JSON.stringify(p.value), at: p.updatedAt })),
|
|
53
|
+
];
|
|
54
|
+
const missing = [];
|
|
55
|
+
if (!jobCases.length)
|
|
56
|
+
missing.push(`客户 ${customer.canonicalName} 无关联职位 — 职位入库时带 --customer`);
|
|
57
|
+
if (jobCase && !prefs.case.length)
|
|
58
|
+
missing.push(`职位 "${jobCase.title ?? jobCase.externalId}" 无 case 层画像偏好 — 寻访后 preference set --scope case`);
|
|
59
|
+
if (!prefs.customer.length)
|
|
60
|
+
missing.push(`客户 ${customer.canonicalName} 无客户层偏好 — 沟通反馈后 preference set --scope customer`);
|
|
61
|
+
if (!remarks.length)
|
|
62
|
+
missing.push(`客户 ${customer.canonicalName} 无任何备注记录 — 推荐/反馈后 note add --target customer --id ${customer.id}`);
|
|
63
|
+
if (remarks.length && !feedbackRemarks.length)
|
|
64
|
+
missing.push('无 category=feedback 的反馈记录(过往推荐/不合适原因)');
|
|
65
|
+
return {
|
|
66
|
+
customer: {
|
|
67
|
+
known: true,
|
|
68
|
+
id: customer.id,
|
|
69
|
+
canonicalName: customer.canonicalName,
|
|
70
|
+
aliases: customer.aliases ?? [],
|
|
71
|
+
},
|
|
72
|
+
positions: jobCases.map((j) => ({ id: j.id, externalId: j.externalId, title: j.title, status: j.status, createdAt: j.createdAt })),
|
|
73
|
+
jobCase: jobCase ? { id: jobCase.id, externalId: jobCase.externalId, title: jobCase.title } : null,
|
|
74
|
+
memory: {
|
|
75
|
+
remarksCount: remarks.length,
|
|
76
|
+
lastRemarkAt: remarks[0]?.createdAt ?? null,
|
|
77
|
+
rejectedReasons,
|
|
78
|
+
},
|
|
79
|
+
preferences: prefs,
|
|
80
|
+
missing,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export const caseCommand = new Command('case').description('职位 case 记忆完整性检查(推荐前自检 / agent 流程判据)');
|
|
84
|
+
caseCommand
|
|
85
|
+
.command('check')
|
|
86
|
+
.requiredOption('--customer <nameOrId>', '客户名或 id')
|
|
87
|
+
.option('--job <externalId>', '职位 externalId 或 id(可选,细查单个 case)')
|
|
88
|
+
.option('--json', 'JSON 输出(默认)')
|
|
89
|
+
.action(async (opts) => {
|
|
90
|
+
try {
|
|
91
|
+
const report = await runCaseCheck(opts.customer, opts.job);
|
|
92
|
+
console.log(JSON.stringify(report, null, 2));
|
|
93
|
+
if (!opts.json && report.missing.length) {
|
|
94
|
+
console.error(`\n⚠ ${report.missing.length} 个记忆缺口(见 missing 字段)`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
printCommandError('case:check-failed', err);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
@@ -3,11 +3,15 @@ export interface ConfigOptions {
|
|
|
3
3
|
serverUrl?: string;
|
|
4
4
|
watchFolder?: string;
|
|
5
5
|
lang?: string;
|
|
6
|
+
dataDir?: string;
|
|
7
|
+
harnessPath?: string;
|
|
6
8
|
}
|
|
7
9
|
export declare function runConfig(options: ConfigOptions): {
|
|
8
10
|
serverUrl: string;
|
|
9
11
|
saasUrl: string;
|
|
10
12
|
lang: string;
|
|
11
13
|
watchFolder?: string;
|
|
14
|
+
dataDir?: string;
|
|
15
|
+
harnessPath?: string;
|
|
12
16
|
};
|
|
13
17
|
export declare const configCommand: Command;
|
package/dist/commands/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { saveConfig } from '../config.js';
|
|
2
|
+
import { saveConfig, resolveDataDir } from '../config.js';
|
|
3
3
|
import { i18n } from '../i18n/index.js';
|
|
4
4
|
export function runConfig(options) {
|
|
5
5
|
const updates = {};
|
|
@@ -9,6 +9,10 @@ export function runConfig(options) {
|
|
|
9
9
|
updates.watchFolder = options.watchFolder;
|
|
10
10
|
if (options.lang)
|
|
11
11
|
updates.lang = options.lang;
|
|
12
|
+
if (options.dataDir)
|
|
13
|
+
updates.dataDir = options.dataDir;
|
|
14
|
+
if (options.harnessPath)
|
|
15
|
+
updates.harnessPath = options.harnessPath;
|
|
12
16
|
const saved = saveConfig(updates);
|
|
13
17
|
return saved;
|
|
14
18
|
}
|
|
@@ -17,7 +21,10 @@ export const configCommand = new Command('config')
|
|
|
17
21
|
.option('--server-url <url>', 'Server URL')
|
|
18
22
|
.option('--watch-folder <path>', 'Watch folder for job cases')
|
|
19
23
|
.option('--lang <lang>', 'Output language')
|
|
24
|
+
.option('--data-dir <path>', '重数据目录(数据库/日志/缓存等);迁移已有数据用 migrate-data-dir')
|
|
25
|
+
.option('--harness-path <path>', 'hunter-mate-harness 仓绝对路径(adapter sync 的 overlay 真相源;env HUNTER_MATE_HARNESS_PATH 优先)')
|
|
20
26
|
.action((options) => {
|
|
21
27
|
runConfig(options);
|
|
22
28
|
console.log(i18n.t('config.saved'));
|
|
29
|
+
console.log(`dataDir: ${resolveDataDir()}`);
|
|
23
30
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export declare function runCustomerUpsert(opts: {
|
|
3
|
+
name: string;
|
|
4
|
+
alias?: string;
|
|
5
|
+
type?: string;
|
|
6
|
+
status?: string;
|
|
7
|
+
notes?: string;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
customer: any;
|
|
10
|
+
created: boolean;
|
|
11
|
+
merged: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function runCustomerList(query?: string): Promise<any[]>;
|
|
14
|
+
export declare function runCustomerRelated(id: number): Promise<{
|
|
15
|
+
customer: any;
|
|
16
|
+
jobCases: any[];
|
|
17
|
+
remarks: any[];
|
|
18
|
+
}>;
|
|
19
|
+
/** 按名称/别名/id 解析客户,返回 related 聚合;未命中返回 null */
|
|
20
|
+
export declare function resolveCustomer(nameOrId: string): Promise<{
|
|
21
|
+
customer: any;
|
|
22
|
+
jobCases: any[];
|
|
23
|
+
remarks: any[];
|
|
24
|
+
} | null>;
|
|
25
|
+
export declare const customerCommand: Command;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { loadConfig } from '../config.js';
|
|
4
|
+
import { getToken } from '../keychain.js';
|
|
5
|
+
import { printCommandError } from '../utils/command-error.js';
|
|
6
|
+
async function authedHeaders() {
|
|
7
|
+
const token = await getToken().catch(() => undefined);
|
|
8
|
+
return token ? { Authorization: `UTOKEN ${token}` } : {};
|
|
9
|
+
}
|
|
10
|
+
function emit(jsonMode, data, human) {
|
|
11
|
+
if (jsonMode) {
|
|
12
|
+
console.log(JSON.stringify(data, null, 2));
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
human();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function runCustomerUpsert(opts) {
|
|
19
|
+
const config = loadConfig();
|
|
20
|
+
const res = await axios.post(`${config.serverUrl}/customers`, {
|
|
21
|
+
name: opts.name,
|
|
22
|
+
aliases: opts.alias ? opts.alias.split(',').map((s) => s.trim()).filter(Boolean) : undefined,
|
|
23
|
+
typeStatus: opts.type || opts.status ? { type: opts.type, status: opts.status } : undefined,
|
|
24
|
+
notes: opts.notes,
|
|
25
|
+
}, { headers: await authedHeaders(), proxy: false });
|
|
26
|
+
return res.data;
|
|
27
|
+
}
|
|
28
|
+
export async function runCustomerList(query) {
|
|
29
|
+
const config = loadConfig();
|
|
30
|
+
const res = await axios.get(`${config.serverUrl}/customers`, {
|
|
31
|
+
params: query ? { query } : {},
|
|
32
|
+
headers: await authedHeaders(),
|
|
33
|
+
proxy: false,
|
|
34
|
+
});
|
|
35
|
+
return res.data;
|
|
36
|
+
}
|
|
37
|
+
export async function runCustomerRelated(id) {
|
|
38
|
+
const config = loadConfig();
|
|
39
|
+
const res = await axios.get(`${config.serverUrl}/customers/${id}/related`, {
|
|
40
|
+
headers: await authedHeaders(),
|
|
41
|
+
proxy: false,
|
|
42
|
+
});
|
|
43
|
+
return res.data;
|
|
44
|
+
}
|
|
45
|
+
/** 按名称/别名/id 解析客户,返回 related 聚合;未命中返回 null */
|
|
46
|
+
export async function resolveCustomer(nameOrId) {
|
|
47
|
+
if (/^\d+$/.test(nameOrId.trim())) {
|
|
48
|
+
try {
|
|
49
|
+
return await runCustomerRelated(parseInt(nameOrId, 10));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const hits = await runCustomerList(nameOrId);
|
|
56
|
+
if (!hits.length)
|
|
57
|
+
return null;
|
|
58
|
+
return runCustomerRelated(hits[0].id);
|
|
59
|
+
}
|
|
60
|
+
export const customerCommand = new Command('customer').description('客户(用人单位)归一化入库与查询 — 推荐记忆闭环锚点');
|
|
61
|
+
customerCommand
|
|
62
|
+
.command('upsert')
|
|
63
|
+
.requiredOption('--name <name>', '客户名(自动归一化去重,命中别名则合并)')
|
|
64
|
+
.option('--alias <a,b>', '额外别名,逗号分隔')
|
|
65
|
+
.option('--type <type>', '客户类型,如 enterprise/startup')
|
|
66
|
+
.option('--status <status>', '合作状态,如 active/paused')
|
|
67
|
+
.option('--notes <notes>', '备注')
|
|
68
|
+
.option('--json', 'JSON 输出(供 agent 解析)')
|
|
69
|
+
.action(async (opts) => {
|
|
70
|
+
try {
|
|
71
|
+
const result = await runCustomerUpsert(opts);
|
|
72
|
+
emit(opts.json, result, () => {
|
|
73
|
+
const { customer, created, merged } = result;
|
|
74
|
+
const tag = created ? '✓ 新建客户' : merged ? '✓ 命中已有客户(已合并别名/信息)' : '✓ 客户已存在';
|
|
75
|
+
console.log(`${tag}: [${customer.id}] ${customer.canonicalName}`);
|
|
76
|
+
if (customer.aliases?.length)
|
|
77
|
+
console.log(` 别名: ${customer.aliases.join(', ')}`);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
printCommandError('customer:upsert-failed', err);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
customerCommand
|
|
86
|
+
.command('list')
|
|
87
|
+
.option('--query <q>', '按名称/别名搜索')
|
|
88
|
+
.option('--json', 'JSON 输出')
|
|
89
|
+
.action(async (opts) => {
|
|
90
|
+
try {
|
|
91
|
+
const list = await runCustomerList(opts.query);
|
|
92
|
+
emit(opts.json, list, () => {
|
|
93
|
+
if (!list.length) {
|
|
94
|
+
console.log('无客户记录');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
console.table(list.map((c) => ({
|
|
98
|
+
id: c.id,
|
|
99
|
+
name: c.canonicalName,
|
|
100
|
+
aliases: (c.aliases ?? []).join(','),
|
|
101
|
+
updatedAt: c.updatedAt,
|
|
102
|
+
})));
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
printCommandError('customer:list-failed', err);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
customerCommand
|
|
111
|
+
.command('show <id>')
|
|
112
|
+
.description('客户详情(含关联职位与备注)')
|
|
113
|
+
.option('--json', 'JSON 输出')
|
|
114
|
+
.action(async (id, opts) => {
|
|
115
|
+
try {
|
|
116
|
+
const data = await runCustomerRelated(parseInt(id, 10));
|
|
117
|
+
emit(opts.json, data, () => {
|
|
118
|
+
const { customer, jobCases, remarks } = data;
|
|
119
|
+
console.log(`[${customer.id}] ${customer.canonicalName}`);
|
|
120
|
+
if (customer.aliases?.length)
|
|
121
|
+
console.log(`别名: ${customer.aliases.join(', ')}`);
|
|
122
|
+
if (customer.notes)
|
|
123
|
+
console.log(`备注: ${customer.notes}`);
|
|
124
|
+
console.log(`职位(${jobCases.length}):`);
|
|
125
|
+
for (const j of jobCases)
|
|
126
|
+
console.log(` - [${j.id}] ${j.title ?? j.externalId} (${j.status})`);
|
|
127
|
+
console.log(`备注记录(${remarks.length}):`);
|
|
128
|
+
for (const r of remarks.slice(0, 10)) {
|
|
129
|
+
console.log(` - [${r.category}] ${String(r.content).slice(0, 80)}`);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
printCommandError('customer:show-failed', err);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
});
|
package/dist/commands/doctor.js
CHANGED
|
@@ -9,7 +9,7 @@ import { runInstall } from './install.js';
|
|
|
9
9
|
import { runExtensionCheck } from './extension.js';
|
|
10
10
|
import { checkNativeModules } from '../native-check.js';
|
|
11
11
|
import { getCliLogger } from '../logger.js';
|
|
12
|
-
import { getToken } from '../keychain.js';
|
|
12
|
+
import { getToken, getTokenInfo } from '../keychain.js';
|
|
13
13
|
const B = {
|
|
14
14
|
reset: '\x1b[0m',
|
|
15
15
|
bold: '\x1b[1m',
|
|
@@ -321,12 +321,24 @@ export async function runDoctor(opts) {
|
|
|
321
321
|
hints.push('hunter-mate login');
|
|
322
322
|
}
|
|
323
323
|
else if (!serverUp) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
324
|
+
const issuer = (await getTokenInfo())?.issuer ?? 'unknown';
|
|
325
|
+
// hmh#43: SaaS 签发的 token 存在性 ok 但 server 起来后必 401,不能笼统报"已登录"
|
|
326
|
+
if (issuer === 'saas') {
|
|
327
|
+
checks.push({
|
|
328
|
+
key: 'auth',
|
|
329
|
+
status: 'warn',
|
|
330
|
+
detail: 'token 由 SaaS 签发(server 未运行,仅验证存在性);server 启动后数据面会 401,请重新登录',
|
|
331
|
+
});
|
|
332
|
+
hints.push('启动 server 后重新登录: hunter-mate login');
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
loggedIn = true;
|
|
336
|
+
checks.push({
|
|
337
|
+
key: 'auth',
|
|
338
|
+
status: 'ok',
|
|
339
|
+
detail: '已登录 (server 未运行, 仅验证 token 存在性)',
|
|
340
|
+
});
|
|
341
|
+
}
|
|
330
342
|
}
|
|
331
343
|
else {
|
|
332
344
|
try {
|
|
@@ -342,8 +354,20 @@ export async function runDoctor(opts) {
|
|
|
342
354
|
const status = e?.response?.status;
|
|
343
355
|
if (status === 401 || status === 403) {
|
|
344
356
|
// 真实 fail: server 明确拒绝了这个 token
|
|
345
|
-
|
|
346
|
-
|
|
357
|
+
const issuer = (await getTokenInfo())?.issuer ?? 'unknown';
|
|
358
|
+
if (issuer === 'saas') {
|
|
359
|
+
// hmh#43: 定向诊断,不再笼统报"登录已过期"
|
|
360
|
+
checks.push({
|
|
361
|
+
key: 'auth',
|
|
362
|
+
status: 'fail',
|
|
363
|
+
detail: `token 由 SaaS 签发,被本地 server 拒绝 (HTTP ${status});登录时本地 server 未运行`,
|
|
364
|
+
});
|
|
365
|
+
hints.push('保持 server 运行后重新登录: hunter-mate login');
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
checks.push({ key: 'auth', status: 'fail', detail: `token 已被 server 拒绝 (HTTP ${status}), 登录已过期或无权访问` });
|
|
369
|
+
hints.push('重新登录: hunter-mate login');
|
|
370
|
+
}
|
|
347
371
|
}
|
|
348
372
|
else {
|
|
349
373
|
// 网络/其他原因: 标 warn,不误报 fail
|
|
@@ -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,7 +295,7 @@ 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,
|
|
298
301
|
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
@@ -305,7 +308,7 @@ async function cmdGoto(url, opts) {
|
|
|
305
308
|
async function cmdEval(expression, opts) {
|
|
306
309
|
return withEnvCheck(async () => {
|
|
307
310
|
const serverUrl = requireServerUrl();
|
|
308
|
-
const r = await (await httpAuth(serverUrl)).post('/extensions/eval', {
|
|
311
|
+
const r = await (await httpAuth(serverUrl, opts.timeout ? Number(opts.timeout) + 15_000 : undefined)).post('/extensions/eval', {
|
|
309
312
|
expression,
|
|
310
313
|
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
311
314
|
extensionId: opts.extensionId,
|
|
@@ -317,7 +320,7 @@ async function cmdEval(expression, opts) {
|
|
|
317
320
|
async function cmdScreenshot(opts) {
|
|
318
321
|
return withEnvCheck(async () => {
|
|
319
322
|
const serverUrl = requireServerUrl();
|
|
320
|
-
const r = await (await httpAuth(serverUrl)).post('/extensions/screenshot', {
|
|
323
|
+
const r = await (await httpAuth(serverUrl, opts.timeout ? Number(opts.timeout) + 15_000 : undefined)).post('/extensions/screenshot', {
|
|
321
324
|
fullPage: opts.full ? true : undefined,
|
|
322
325
|
selector: opts.selector,
|
|
323
326
|
format: opts.imageFormat ?? 'png',
|
|
@@ -354,7 +357,7 @@ async function cmdRun(site, command, opts) {
|
|
|
354
357
|
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
355
358
|
extensionId: opts.extensionId,
|
|
356
359
|
};
|
|
357
|
-
const r = await (await httpAuth(serverUrl)).post(endpoint, payload);
|
|
360
|
+
const r = await (await httpAuth(serverUrl, opts.timeout ? Number(opts.timeout) + 15_000 : undefined)).post(endpoint, payload);
|
|
358
361
|
printResult(r.data, opts.format);
|
|
359
362
|
});
|
|
360
363
|
}
|
|
@@ -382,7 +385,7 @@ extensionCommand
|
|
|
382
385
|
console.log(`\n${B.green}✓ Hunter Mate 扩展已连接 (${count} 个浏览器实例)${B.reset}\n`);
|
|
383
386
|
}
|
|
384
387
|
catch (err) {
|
|
385
|
-
printCommandError('extension:check-error', err);
|
|
388
|
+
await printCommandError('extension:check-error', err);
|
|
386
389
|
process.exit(1);
|
|
387
390
|
}
|
|
388
391
|
});
|
|
@@ -410,6 +413,6 @@ common(extensionCommand
|
|
|
410
413
|
.action(async (opts) => cmdScreenshot(opts));
|
|
411
414
|
common(extensionCommand
|
|
412
415
|
.command('run <site> <command>')
|
|
413
|
-
.description('站点适配器命令 (对应 chromepilot run): linkedin search|extract|invite 或 resume extract'))
|
|
416
|
+
.description('站点适配器命令 (对应 chromepilot run): linkedin search|extract|invite、liepin search|extract 或 resume extract'))
|
|
414
417
|
.option('--args <json>', '命令参数 JSON 字符串')
|
|
415
418
|
.action(async (site, command, opts) => cmdRun(site, command, opts));
|
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* FID-092:扫描扩展目录最新 mtime,等待其静止——reload 广播前确认 dist 已写完。
|
|
4
|
+
* 背景:R5 实机 reload 后 3 秒 dist 仍在写入,Chrome 重读读到写了一半的
|
|
5
|
+
* service-worker.js → SW eval 崩溃 → 无 alarm 监听/无 offscreen/零重连,
|
|
6
|
+
* 所有自愈路径全部失效,只能人工点。Windows 兼容:纯 node fs,无 shell。
|
|
7
|
+
*/
|
|
8
|
+
export declare function waitForExtensionDirQuiet(dir: string, opts?: {
|
|
9
|
+
quietMs?: number;
|
|
10
|
+
maxWaitMs?: number;
|
|
11
|
+
pollMs?: number;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
quiet: boolean;
|
|
14
|
+
waitedMs: number;
|
|
15
|
+
newestMtimeMs: number;
|
|
16
|
+
}>;
|
|
17
|
+
/** FID-092:轮询 server 已连接扩展数(>=1 即判定 reload 后已回连)。 */
|
|
18
|
+
export declare function extensionConnectedCount(serverUrl: string, timeoutMs?: number): Promise<number>;
|
|
19
|
+
/**
|
|
20
|
+
* 通知所有已连接扩展 reload(自愈主链路),FID-092 三段式:
|
|
21
|
+
* 1) dist 静止确认(防 reload 读到写一半的扩展文件)
|
|
22
|
+
* 2) 广播(broadcastReload):优先 HTTP POST /system/broadcast-extension;
|
|
23
|
+
* 旧 server 无该端点时退回直连 WS /ws 广播
|
|
24
|
+
* 3) 健康校验 + 自动重试(广播后 12s 内未回连 → 再广播,最多 3 次)
|
|
6
25
|
*/
|
|
7
26
|
export declare function runReload(timeoutMs?: number): Promise<{
|
|
8
27
|
broadcast: number;
|