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,75 @@
|
|
|
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
|
+
const TARGETS = ['customer', 'job', 'talent', 'company', 'talent-job-match'];
|
|
7
|
+
async function authedHeaders() {
|
|
8
|
+
const token = await getToken().catch(() => undefined);
|
|
9
|
+
return token ? { Authorization: `UTOKEN ${token}` } : {};
|
|
10
|
+
}
|
|
11
|
+
/** 业务备注(Remark 实体)CLI 入口:推荐反馈/沟通结论/不合适原因等记忆载体 */
|
|
12
|
+
export const noteCommand = new Command('note').description('业务备注:挂在 customer/job/talent/company 上的记忆记录(Remark)');
|
|
13
|
+
noteCommand
|
|
14
|
+
.command('add')
|
|
15
|
+
.requiredOption('--target <type>', `目标类型: ${TARGETS.join('|')}`)
|
|
16
|
+
.requiredOption('--id <targetId>', '目标 id')
|
|
17
|
+
.requiredOption('--content <text>', '备注内容')
|
|
18
|
+
.option('--category <cat>', '分类,如 feedback/general/interview', 'general')
|
|
19
|
+
.option('--tags <a,b>', '标签,逗号分隔')
|
|
20
|
+
.option('--json', 'JSON 输出')
|
|
21
|
+
.action(async (opts) => {
|
|
22
|
+
try {
|
|
23
|
+
if (!TARGETS.includes(opts.target)) {
|
|
24
|
+
throw new Error(`--target 必须是: ${TARGETS.join('|')}`);
|
|
25
|
+
}
|
|
26
|
+
const config = loadConfig();
|
|
27
|
+
const res = await axios.post(`${config.serverUrl}/remarks`, {
|
|
28
|
+
targetType: opts.target,
|
|
29
|
+
targetId: String(opts.id),
|
|
30
|
+
author: { userId: 'cli', userName: 'cli', source: 'cli' },
|
|
31
|
+
content: opts.content,
|
|
32
|
+
category: opts.category,
|
|
33
|
+
tags: opts.tags ? opts.tags.split(',').map((s) => s.trim()).filter(Boolean) : undefined,
|
|
34
|
+
}, { headers: await authedHeaders(), proxy: false });
|
|
35
|
+
if (opts.json)
|
|
36
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
37
|
+
else
|
|
38
|
+
console.log(`✓ 备注已记录 (${opts.target}#${opts.id}, category=${opts.category})`);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
printCommandError('note:add-failed', err);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
noteCommand
|
|
46
|
+
.command('list')
|
|
47
|
+
.requiredOption('--target <type>', `目标类型: ${TARGETS.join('|')}`)
|
|
48
|
+
.requiredOption('--id <targetId>', '目标 id')
|
|
49
|
+
.option('--json', 'JSON 输出')
|
|
50
|
+
.action(async (opts) => {
|
|
51
|
+
try {
|
|
52
|
+
if (!TARGETS.includes(opts.target)) {
|
|
53
|
+
throw new Error(`--target 必须是: ${TARGETS.join('|')}`);
|
|
54
|
+
}
|
|
55
|
+
const config = loadConfig();
|
|
56
|
+
const res = await axios.get(`${config.serverUrl}/remarks/${opts.target}/${encodeURIComponent(String(opts.id))}`, { headers: await authedHeaders(), proxy: false });
|
|
57
|
+
const list = Array.isArray(res.data) ? res.data : [];
|
|
58
|
+
if (opts.json) {
|
|
59
|
+
console.log(JSON.stringify(list, null, 2));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
if (!list.length) {
|
|
63
|
+
console.log(`无备注 (${opts.target}#${opts.id})`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (const r of list) {
|
|
67
|
+
console.log(`- [${r.category}] ${String(r.content).slice(0, 100)} (${r.createdAt})`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
printCommandError('note:list-failed', err);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
});
|
package/dist/commands/ops.js
CHANGED
|
@@ -2,8 +2,9 @@ import { Command } from 'commander';
|
|
|
2
2
|
import axios from 'axios';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
-
import { loadConfig } from '../config.js';
|
|
5
|
+
import { loadConfig, resolveDataDir } from '../config.js';
|
|
6
6
|
import { getCliLogger } from '../logger.js';
|
|
7
|
+
import { printCommandError } from '../utils/command-error.js';
|
|
7
8
|
// OpenCLI 风格的通用选项
|
|
8
9
|
function withFormat(cmd) {
|
|
9
10
|
return cmd.option('-f, --format <format>', '输出格式: table|json|yaml|md|csv', 'json');
|
|
@@ -76,8 +77,37 @@ withFormat(recordingsCommand.command('list').description('列出录制会话(
|
|
|
76
77
|
printResult(r.data, opts.format);
|
|
77
78
|
}
|
|
78
79
|
catch (e) {
|
|
79
|
-
//
|
|
80
|
-
|
|
80
|
+
// hmh#44: /api/recordings GET 早已实现,连接失败应如实报错,不再回退到假提示
|
|
81
|
+
await printCommandError('ops:recordings-list-failed', e);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
/** hmh#44: 按会话删除录制数据(DELETE /api/recordings/:sessionId) */
|
|
86
|
+
recordingsCommand
|
|
87
|
+
.command('delete <sessionId>')
|
|
88
|
+
.description('删除指定录制会话的事件与快照 (DELETE /api/recordings/:id)')
|
|
89
|
+
.action(async (sessionId) => {
|
|
90
|
+
try {
|
|
91
|
+
const r = await http().delete(`/api/recordings/${sessionId}`);
|
|
92
|
+
console.log(JSON.stringify(r.data, null, 2));
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
await printCommandError('ops:recordings-delete-failed', e);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
/** hmh#44: 手动 VACUUM 缩容,删除数据后让 SQLite 文件真实释放磁盘 */
|
|
100
|
+
recordingsCommand
|
|
101
|
+
.command('compact')
|
|
102
|
+
.description('VACUUM 压缩录制库,释放已删除数据占用的磁盘 (POST /api/recordings/compact)')
|
|
103
|
+
.action(async () => {
|
|
104
|
+
try {
|
|
105
|
+
const r = await http().post('/api/recordings/compact', {});
|
|
106
|
+
console.log(JSON.stringify(r.data, null, 2));
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
await printCommandError('ops:recordings-compact-failed', e);
|
|
110
|
+
process.exit(1);
|
|
81
111
|
}
|
|
82
112
|
});
|
|
83
113
|
withFormat(recordingsCommand.command('show <sessionId>').description('查看录制详情 (/api/recordings/:id) + 可选 parse'))
|
|
@@ -115,11 +145,12 @@ recordingsCommand
|
|
|
115
145
|
// adapter (OpenCLI 风格)
|
|
116
146
|
// ---------------------------------------------------------------------------
|
|
117
147
|
export const adapterCommand = withFormat(new Command('adapter')
|
|
118
|
-
.description('站点适配器(对应 chromepilot adapter): list | extract <url>')
|
|
119
|
-
.argument('[action]', '操作: list | extract', 'list')
|
|
148
|
+
.description('站点适配器(对应 chromepilot adapter): list | extract <url> | sync')
|
|
149
|
+
.argument('[action]', '操作: list | extract | sync', 'list')
|
|
120
150
|
.argument('[target]', 'list: 站点名过滤;extract: 页面 URL', '')
|
|
121
151
|
.option('--tab <pageId>', 'extract: 指定目标 tab(page id;默认复用同站点 tab 或新建后台 tab,不抢焦点)')
|
|
122
|
-
.option('--settle-ms <n>', 'extract: 导航后首查等待 ms(默认 1200)', '1200')
|
|
152
|
+
.option('--settle-ms <n>', 'extract: 导航后首查等待 ms(默认 1200)', '1200')
|
|
153
|
+
.option('--dry-run', 'sync: 只统计差异不写库', false)).action(async (action, target, opts) => {
|
|
123
154
|
if (action === 'list' || !action) {
|
|
124
155
|
try {
|
|
125
156
|
const r = await http().get('/adapters');
|
|
@@ -132,6 +163,7 @@ export const adapterCommand = withFormat(new Command('adapter')
|
|
|
132
163
|
// server 暂未实现 /adapters 时退回内置清单
|
|
133
164
|
printResult({ note: `GET /adapters failed (${e.message}),fallback 内置清单`, adapters: [
|
|
134
165
|
{ platform: 'linkedin', host: 'linkedin.com', command: 'hm.linkedin.extract' },
|
|
166
|
+
{ platform: 'liepin', host: 'h.liepin.com', command: 'hm.liepin.extract' },
|
|
135
167
|
] }, opts.format);
|
|
136
168
|
}
|
|
137
169
|
return;
|
|
@@ -168,7 +200,29 @@ export const adapterCommand = withFormat(new Command('adapter')
|
|
|
168
200
|
}
|
|
169
201
|
return;
|
|
170
202
|
}
|
|
171
|
-
|
|
203
|
+
if (action === 'sync') {
|
|
204
|
+
// hunter-mate-harness adapters/ overlay 同步 @fid FID-081-adapter-overlay-suite
|
|
205
|
+
// 路径优先级: env HUNTER_MATE_HARNESS_PATH > config.json harnessPath
|
|
206
|
+
const cfg = loadConfig();
|
|
207
|
+
const harnessPath = process.env.HUNTER_MATE_HARNESS_PATH || cfg?.harnessPath;
|
|
208
|
+
if (!harnessPath || !harnessPath.trim()) {
|
|
209
|
+
getCliLogger().error('ops:adapter-sync-missing-harness-path', {
|
|
210
|
+
hint: '先配置: hunter-mate config --harness-path <hunter-mate-harness 仓绝对路径>(或设 env HUNTER_MATE_HARNESS_PATH)',
|
|
211
|
+
});
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const r = await http().post('/adapters/sync', { harnessPath, dryRun: !!opts.dryRun });
|
|
216
|
+
printResult(r.data, opts.format);
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
const detail = e.response?.data?.message || e.message;
|
|
220
|
+
console.error(`✗ ops:adapter-sync-failed: ${detail}`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
printResult({ error: `unknown action: ${action}`, supported: ['list', 'extract', 'sync'] }, opts.format);
|
|
172
226
|
});
|
|
173
227
|
// ---------------------------------------------------------------------------
|
|
174
228
|
// memory (站点记忆: 基于 selector/流程建议 的持久化 KV)
|
|
@@ -178,8 +232,7 @@ export const memoryCommand = withFormat(new Command('memory')
|
|
|
178
232
|
.argument('[action]', '操作 (默认 list)', 'list')
|
|
179
233
|
.argument('[site]', '站点名', '')
|
|
180
234
|
.argument('[ref]', 'suggest/delete 的元素引用', '')).action((action, site, ref, opts) => {
|
|
181
|
-
const
|
|
182
|
-
const STORE = path.join(HOME, '.hunter-mate', 'memory.json');
|
|
235
|
+
const STORE = path.join(resolveDataDir(), 'memory.json');
|
|
183
236
|
let store = {};
|
|
184
237
|
try {
|
|
185
238
|
store = JSON.parse(fs.readFileSync(STORE, 'utf-8'));
|
|
@@ -304,8 +357,7 @@ export const traceCommand = withFormat(new Command('trace')
|
|
|
304
357
|
}
|
|
305
358
|
catch (e) {
|
|
306
359
|
// server 未就绪时: 本地追加 fallbacks 到 session JSONL 文件
|
|
307
|
-
const
|
|
308
|
-
const f = path.join(HOME, '.hunter-mate', 'sessions', `${sessionId}.trace.jsonl`);
|
|
360
|
+
const f = path.join(resolveDataDir(), 'sessions', `${sessionId}.trace.jsonl`);
|
|
309
361
|
fs.appendFileSync(f, JSON.stringify({ type: opts.type, payload, ts: Date.now() }) + '\n');
|
|
310
362
|
printResult({ ok: true, stored: 'local', file: f }, opts.format);
|
|
311
363
|
}
|
|
@@ -318,8 +370,7 @@ export const traceCommand = withFormat(new Command('trace')
|
|
|
318
370
|
}
|
|
319
371
|
printResult({ ok: true, sessionId, note: '本地 trace 文件删除(server 侧请通过 api/recordings/* 管理)' }, opts.format);
|
|
320
372
|
try {
|
|
321
|
-
const
|
|
322
|
-
const f = path.join(HOME, '.hunter-mate', 'sessions', `${sessionId}.trace.jsonl`);
|
|
373
|
+
const f = path.join(resolveDataDir(), 'sessions', `${sessionId}.trace.jsonl`);
|
|
323
374
|
if (fs.existsSync(f))
|
|
324
375
|
fs.unlinkSync(f);
|
|
325
376
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
const SCOPES = ['user', 'customer', 'case'];
|
|
7
|
+
async function authedHeaders() {
|
|
8
|
+
const token = await getToken().catch(() => undefined);
|
|
9
|
+
return token ? { Authorization: `UTOKEN ${token}` } : {};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 三层偏好:scope=user(顾问本人)/ customer(用人单位)/ case(具体职位)。
|
|
13
|
+
* 查询 case 层时自动按 case → customer → user 逐层 merge,按 weight 降序。
|
|
14
|
+
*/
|
|
15
|
+
export const preferenceCommand = new Command('preference').description('三层偏好沉淀(user/customer/case),用于人才推荐筛选');
|
|
16
|
+
preferenceCommand
|
|
17
|
+
.command('set')
|
|
18
|
+
.requiredOption('--scope <scope>', `范围: ${SCOPES.join('|')}`)
|
|
19
|
+
.requiredOption('--id <scopeId>', '范围 id(user id / customer id / job case id)')
|
|
20
|
+
.requiredOption('--dimension <dim>', '维度,如 industry/comp_size/school_tier/reject_reason/must_have')
|
|
21
|
+
.requiredOption('--value <json>', '偏好值(JSON 字符串,如 \'"electronics"\' 或 \'{"min":5}\')')
|
|
22
|
+
.option('--weight <w>', '权重(默认 1)', parseFloat)
|
|
23
|
+
.option('--evidence <text>', '证据来源:哪次推荐/哪条反馈')
|
|
24
|
+
.option('--json', 'JSON 输出')
|
|
25
|
+
.action(async (opts) => {
|
|
26
|
+
try {
|
|
27
|
+
if (!SCOPES.includes(opts.scope)) {
|
|
28
|
+
throw new Error(`--scope 必须是: ${SCOPES.join('|')}`);
|
|
29
|
+
}
|
|
30
|
+
let value;
|
|
31
|
+
try {
|
|
32
|
+
value = JSON.parse(opts.value);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new Error(`--value 必须是合法 JSON 字符串: ${opts.value}`);
|
|
36
|
+
}
|
|
37
|
+
const config = loadConfig();
|
|
38
|
+
const res = await axios.put(`${config.serverUrl}/preferences`, {
|
|
39
|
+
scope: opts.scope,
|
|
40
|
+
scopeId: parseInt(opts.id, 10),
|
|
41
|
+
items: [{ dimension: opts.dimension, value, weight: opts.weight, evidence: opts.evidence }],
|
|
42
|
+
}, { headers: await authedHeaders(), proxy: false });
|
|
43
|
+
if (opts.json)
|
|
44
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
45
|
+
else
|
|
46
|
+
console.log(`✓ 偏好已写入 (${opts.scope}#${opts.id}, ${opts.dimension}=${opts.value})`);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
printCommandError('preference:set-failed', err);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
preferenceCommand
|
|
54
|
+
.command('get')
|
|
55
|
+
.requiredOption('--scope <scope>', `范围: ${SCOPES.join('|')}`)
|
|
56
|
+
.requiredOption('--id <scopeId>', '范围 id')
|
|
57
|
+
.option('--customer-id <id>', 'scope=case 时传客户 id,一并带出 customer/user 两层')
|
|
58
|
+
.option('--json', 'JSON 输出(默认 JSON:三层 merge 结构)')
|
|
59
|
+
.action(async (opts) => {
|
|
60
|
+
try {
|
|
61
|
+
if (!SCOPES.includes(opts.scope)) {
|
|
62
|
+
throw new Error(`--scope 必须是: ${SCOPES.join('|')}`);
|
|
63
|
+
}
|
|
64
|
+
const config = loadConfig();
|
|
65
|
+
const res = await axios.get(`${config.serverUrl}/preferences`, {
|
|
66
|
+
params: {
|
|
67
|
+
scope: opts.scope,
|
|
68
|
+
scopeId: parseInt(opts.id, 10),
|
|
69
|
+
...(opts.customerId ? { customerId: parseInt(opts.customerId, 10) } : {}),
|
|
70
|
+
},
|
|
71
|
+
headers: await authedHeaders(),
|
|
72
|
+
proxy: false,
|
|
73
|
+
});
|
|
74
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
printCommandError('preference:get-failed', err);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
});
|
package/dist/commands/record.js
CHANGED
package/dist/commands/report.js
CHANGED
package/dist/commands/search.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* server.log 无界增长曾达 800M+ 挤占用户磁盘(FID-068)。
|
|
4
|
+
* 后台 daemon 启动前按大小轮转:超过 HM_LOG_MAX_BYTES(默认 50MB)时
|
|
5
|
+
* server.log → server.log.1 → …保留最近 LOG_KEEP_FILES 份,最旧丢弃。
|
|
6
|
+
* 纯 fs 实现,Windows 兼容(rename 前先清目标,Windows 不允许覆盖已有文件)。
|
|
7
|
+
*/
|
|
8
|
+
export declare function rotateLogIfNeeded(logFile: string, maxBytes?: number, keep?: number): boolean;
|
|
2
9
|
declare function tryReadPid(): number | null;
|
|
3
10
|
export { tryReadPid };
|
|
4
11
|
/** 定位 server 包根(dist 优先,ts-node 开发模式兜底);找不到返回 null */
|
package/dist/commands/server.js
CHANGED
|
@@ -3,11 +3,12 @@ import * as fs from 'fs';
|
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
|
-
import { loadConfig, saveConfig } from '../config.js';
|
|
6
|
+
import { loadConfig, saveConfig, resolveDataDir } from '../config.js';
|
|
7
7
|
import { killGracefullySync } from '../utils/proc.js';
|
|
8
8
|
import { findBundledAssets } from '../bundled-assets.js';
|
|
9
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
|
|
10
|
+
/** server.pid 等运行时文件随数据目录走(FID-071:dataDir 可配置后 server 状态文件一并迁移) */
|
|
11
|
+
const PID_FILE = () => path.join(resolveDataDir(), 'server.pid');
|
|
11
12
|
const LOG_FILE = () => PID_FILE().replace(/server\.pid$/, 'server.log');
|
|
12
13
|
const WATCHDOG_PID_FILE = () => PID_FILE().replace(/server\.pid$/, 'watchdog.pid');
|
|
13
14
|
function tryReadPidFile(file) {
|
|
@@ -29,6 +30,38 @@ function cleanPidFile(file) {
|
|
|
29
30
|
}
|
|
30
31
|
catch { /* ignore */ }
|
|
31
32
|
}
|
|
33
|
+
const LOG_MAX_BYTES = () => Number(process.env.HM_LOG_MAX_BYTES || 50 * 1024 * 1024);
|
|
34
|
+
const LOG_KEEP_FILES = 3;
|
|
35
|
+
/**
|
|
36
|
+
* server.log 无界增长曾达 800M+ 挤占用户磁盘(FID-068)。
|
|
37
|
+
* 后台 daemon 启动前按大小轮转:超过 HM_LOG_MAX_BYTES(默认 50MB)时
|
|
38
|
+
* server.log → server.log.1 → …保留最近 LOG_KEEP_FILES 份,最旧丢弃。
|
|
39
|
+
* 纯 fs 实现,Windows 兼容(rename 前先清目标,Windows 不允许覆盖已有文件)。
|
|
40
|
+
*/
|
|
41
|
+
export function rotateLogIfNeeded(logFile, maxBytes = LOG_MAX_BYTES(), keep = LOG_KEEP_FILES) {
|
|
42
|
+
try {
|
|
43
|
+
if (!fs.existsSync(logFile))
|
|
44
|
+
return false;
|
|
45
|
+
if (fs.statSync(logFile).size <= maxBytes)
|
|
46
|
+
return false;
|
|
47
|
+
for (let i = keep - 1; i >= 1; i--) {
|
|
48
|
+
const src = `${logFile}.${i}`;
|
|
49
|
+
const dest = `${logFile}.${i + 1}`;
|
|
50
|
+
if (!fs.existsSync(src))
|
|
51
|
+
continue;
|
|
52
|
+
try {
|
|
53
|
+
fs.unlinkSync(dest);
|
|
54
|
+
}
|
|
55
|
+
catch { /* dest 不存在时忽略 */ }
|
|
56
|
+
fs.renameSync(src, dest);
|
|
57
|
+
}
|
|
58
|
+
fs.renameSync(logFile, `${logFile}.1`);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
32
65
|
function tryReadPid() {
|
|
33
66
|
const pid = tryReadPidFile(PID_FILE());
|
|
34
67
|
if (pid)
|
|
@@ -131,6 +164,8 @@ export async function runStart(opts) {
|
|
|
131
164
|
...process.env,
|
|
132
165
|
PORT: String(port),
|
|
133
166
|
HOST: host,
|
|
167
|
+
// FID-071: server 所有数据落点(db/备份/runtime.json/artifacts)由 CLI 的数据目录决定
|
|
168
|
+
HM_DATA_DIR: resolveDataDir(),
|
|
134
169
|
};
|
|
135
170
|
const nodeArgs = [];
|
|
136
171
|
if (entry.endsWith('.ts')) {
|
|
@@ -148,6 +183,8 @@ export async function runStart(opts) {
|
|
|
148
183
|
const logDir = path.dirname(LOG_FILE());
|
|
149
184
|
if (!fs.existsSync(logDir))
|
|
150
185
|
fs.mkdirSync(logDir, { recursive: true });
|
|
186
|
+
// 后台 daemon 启动前轮转 server.log(超 50MB 保留 3 份),防止无界增长挤占磁盘
|
|
187
|
+
rotateLogIfNeeded(LOG_FILE());
|
|
151
188
|
// 后台模式由独立 watchdog 进程托管 server,实现崩溃后自动重启
|
|
152
189
|
const watchdogEntry = path.resolve(__dirname, '../watchdog.js');
|
|
153
190
|
if (!fs.existsSync(watchdogEntry)) {
|
|
@@ -163,6 +200,7 @@ export async function runStart(opts) {
|
|
|
163
200
|
HM_SERVER_ENTRY: entry,
|
|
164
201
|
HM_SERVER_LOG: LOG_FILE(),
|
|
165
202
|
HM_SERVER_NODE_ARGS: nodeArgs.join(','),
|
|
203
|
+
HM_DATA_DIR: resolveDataDir(),
|
|
166
204
|
PORT: String(port),
|
|
167
205
|
HOST: host,
|
|
168
206
|
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { TokenIssuer } from '../keychain.js';
|
|
2
3
|
import { InstalledVersions, SystemVersions } from '@hunter-mate/shared';
|
|
3
4
|
export interface StatusResult {
|
|
4
5
|
version: string;
|
|
5
6
|
loggedIn: boolean;
|
|
7
|
+
/** token 签发方(hmh#43):saas 签发的 token 会被本地 server 拒绝 */
|
|
8
|
+
tokenIssuer?: TokenIssuer;
|
|
6
9
|
server: 'running' | 'not running' | 'unknown';
|
|
7
10
|
serverUrl: string;
|
|
8
11
|
extensionCount: number;
|
|
@@ -15,6 +18,7 @@ export interface StatusResult {
|
|
|
15
18
|
}>;
|
|
16
19
|
installed?: InstalledVersions;
|
|
17
20
|
live?: SystemVersions | null;
|
|
21
|
+
dataDir: string;
|
|
18
22
|
}
|
|
19
23
|
export declare function runStatus(): Promise<StatusResult>;
|
|
20
24
|
export declare const statusCommand: Command;
|
package/dist/commands/status.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import axios from 'axios';
|
|
3
3
|
import { i18n } from '../i18n/index.js';
|
|
4
|
-
import { loadConfig } from '../config.js';
|
|
5
|
-
import {
|
|
4
|
+
import { loadConfig, resolveDataDir } from '../config.js';
|
|
5
|
+
import { getTokenInfo } from '../keychain.js';
|
|
6
6
|
import { readInstalledVersions, fetchLiveVersions } from '../version.js';
|
|
7
7
|
import { getCliLogger } from '../logger.js';
|
|
8
8
|
export async function runStatus() {
|
|
9
9
|
const config = loadConfig();
|
|
10
|
-
const
|
|
10
|
+
const tokenInfo = await getTokenInfo();
|
|
11
|
+
const token = tokenInfo?.token ?? null;
|
|
11
12
|
let serverStatus = 'unknown';
|
|
12
13
|
let extensions = [];
|
|
13
14
|
let live = null;
|
|
@@ -28,15 +29,18 @@ export async function runStatus() {
|
|
|
28
29
|
serverStatus = 'not running';
|
|
29
30
|
getCliLogger().warn('status:server-not-running', { serverUrl: config.serverUrl });
|
|
30
31
|
}
|
|
32
|
+
const installed = readInstalledVersions();
|
|
31
33
|
return {
|
|
32
|
-
version: '0.
|
|
34
|
+
version: installed.cli ?? '0.0.0',
|
|
33
35
|
loggedIn: !!token,
|
|
36
|
+
tokenIssuer: tokenInfo?.issuer,
|
|
34
37
|
server: serverStatus,
|
|
35
38
|
serverUrl: config.serverUrl,
|
|
36
39
|
extensionCount: extensions.length,
|
|
37
40
|
extensions,
|
|
38
|
-
installed
|
|
41
|
+
installed,
|
|
39
42
|
live,
|
|
43
|
+
dataDir: resolveDataDir(),
|
|
40
44
|
};
|
|
41
45
|
}
|
|
42
46
|
export const statusCommand = new Command('status')
|
|
@@ -49,8 +53,14 @@ export const statusCommand = new Command('status')
|
|
|
49
53
|
return;
|
|
50
54
|
}
|
|
51
55
|
console.log(`${i18n.t('status.cli')}: ${status.version}`);
|
|
52
|
-
|
|
56
|
+
const loggedInText = status.loggedIn
|
|
57
|
+
? status.tokenIssuer === 'saas'
|
|
58
|
+
? 'yes (SaaS 签发 — 本地 server 会拒绝,启动 server 后请重新登录)'
|
|
59
|
+
: 'yes'
|
|
60
|
+
: 'no';
|
|
61
|
+
console.log(`${i18n.t('status.loggedIn')}: ${loggedInText}`);
|
|
53
62
|
console.log(`${i18n.t('status.server')}: ${status.server} (${status.serverUrl})`);
|
|
63
|
+
console.log(`dataDir: ${status.dataDir}`);
|
|
54
64
|
// 版本一致性: 磁盘(期望) vs 运行中
|
|
55
65
|
if (status.installed) {
|
|
56
66
|
const inst = status.installed;
|
package/dist/commands/update.js
CHANGED
|
@@ -2,29 +2,28 @@ import { Command } from 'commander';
|
|
|
2
2
|
import axios from 'axios';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
-
import { loadConfig } from '../config.js';
|
|
5
|
+
import { loadConfig, resolveDataDir } from '../config.js';
|
|
6
6
|
import { getToken } from '../keychain.js';
|
|
7
7
|
import { i18n } from '../i18n/index.js';
|
|
8
8
|
import { getCliLogger } from '../logger.js';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
10
|
import { readInstalledVersions, ensureRuntimeConsistency } from '../version.js';
|
|
11
11
|
import { isDevLinked, npmLatestVersion, npmSelfUpdate } from '../updater.js';
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const VERSION_FILE = path.join(RULES_DIR, 'version.json');
|
|
12
|
+
const RULES_DIR = () => path.join(resolveDataDir(), 'rules');
|
|
13
|
+
const VERSION_FILE = () => path.join(RULES_DIR(), 'version.json');
|
|
15
14
|
function loadLocalVersion() {
|
|
16
|
-
if (!fs.existsSync(VERSION_FILE))
|
|
15
|
+
if (!fs.existsSync(VERSION_FILE()))
|
|
17
16
|
return null;
|
|
18
17
|
try {
|
|
19
|
-
return JSON.parse(fs.readFileSync(VERSION_FILE, 'utf-8'));
|
|
18
|
+
return JSON.parse(fs.readFileSync(VERSION_FILE(), 'utf-8'));
|
|
20
19
|
}
|
|
21
20
|
catch {
|
|
22
21
|
return null;
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
function saveLocalVersion(version) {
|
|
26
|
-
fs.mkdirSync(RULES_DIR, { recursive: true });
|
|
27
|
-
fs.writeFileSync(VERSION_FILE, JSON.stringify({ version, updatedAt: new Date().toISOString() }, null, 2));
|
|
25
|
+
fs.mkdirSync(RULES_DIR(), { recursive: true });
|
|
26
|
+
fs.writeFileSync(VERSION_FILE(), JSON.stringify({ version, updatedAt: new Date().toISOString() }, null, 2));
|
|
28
27
|
}
|
|
29
28
|
export async function runUpdate() {
|
|
30
29
|
const config = loadConfig();
|
package/dist/config.d.ts
CHANGED
|
@@ -5,9 +5,28 @@ export interface HunterMateConfig {
|
|
|
5
5
|
saasUrl: string;
|
|
6
6
|
lang: string;
|
|
7
7
|
watchFolder?: string;
|
|
8
|
+
/**
|
|
9
|
+
* 重数据目录(数据库/日志/缓存/扩展/CLI 安装树等),不含 config.json 本身。
|
|
10
|
+
* 通过 `hunter-mate migrate-data-dir <dir>` 迁移;留空表示默认 ~/.hunter-mate。
|
|
11
|
+
*/
|
|
12
|
+
dataDir?: string;
|
|
13
|
+
/**
|
|
14
|
+
* hunter-mate-harness 仓绝对路径(外部 adapters overlay 真相源)。
|
|
15
|
+
* `hunter-mate adapter sync` 读取;env HUNTER_MATE_HARNESS_PATH 优先级更高。 @fid FID-081-adapter-overlay-suite
|
|
16
|
+
*/
|
|
17
|
+
harnessPath?: string;
|
|
8
18
|
}
|
|
9
19
|
export declare const CONFIG_DIR: () => string;
|
|
10
20
|
export declare const CONFIG_FILE: () => string;
|
|
21
|
+
/**
|
|
22
|
+
* 重数据目录解析(FID-071):
|
|
23
|
+
* HM_DATA_DIR(env,进程级覆盖,CLI spawn server 时注入) > config.dataDir(用户配置) > 默认 CONFIG_DIR。
|
|
24
|
+
* config.json 本身始终留在 CONFIG_DIR(KB 级不迁移),保证任何时刻都能定位配置。
|
|
25
|
+
* 注意:每次调用都实时读取(不缓存),config --data-dir 写入后同一进程内立即生效。
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveDataDir(): string;
|
|
28
|
+
/** resolveDataDir 的别名,语义化表达"数据目录"(与 CONFIG_DIR 配置目录区分) */
|
|
29
|
+
export declare const DATA_DIR: typeof resolveDataDir;
|
|
11
30
|
/**
|
|
12
31
|
* 已知废弃的 URL 路径前缀(按部署形态演进顺序追加)。
|
|
13
32
|
* 每当 nginx / 服务端路径收敛(如子路径 → 根路径)时,把旧前缀加到这里,
|
|
@@ -43,4 +62,6 @@ export declare function saveConfig(config: Partial<HunterMateConfig>): {
|
|
|
43
62
|
saasUrl: string;
|
|
44
63
|
lang: string;
|
|
45
64
|
watchFolder?: string;
|
|
65
|
+
dataDir?: string;
|
|
66
|
+
harnessPath?: string;
|
|
46
67
|
};
|
package/dist/config.js
CHANGED
|
@@ -4,6 +4,29 @@ import { createJsonConfigStore } from '@chromepilot/kit';
|
|
|
4
4
|
const HOME = () => process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
|
|
5
5
|
export const CONFIG_DIR = () => path.join(HOME(), '.hunter-mate');
|
|
6
6
|
export const CONFIG_FILE = () => path.join(CONFIG_DIR(), 'config.json');
|
|
7
|
+
/**
|
|
8
|
+
* 重数据目录解析(FID-071):
|
|
9
|
+
* HM_DATA_DIR(env,进程级覆盖,CLI spawn server 时注入) > config.dataDir(用户配置) > 默认 CONFIG_DIR。
|
|
10
|
+
* config.json 本身始终留在 CONFIG_DIR(KB 级不迁移),保证任何时刻都能定位配置。
|
|
11
|
+
* 注意:每次调用都实时读取(不缓存),config --data-dir 写入后同一进程内立即生效。
|
|
12
|
+
*/
|
|
13
|
+
export function resolveDataDir() {
|
|
14
|
+
if (process.env.HM_DATA_DIR && process.env.HM_DATA_DIR.trim()) {
|
|
15
|
+
return path.resolve(process.env.HM_DATA_DIR);
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const raw = store.load();
|
|
19
|
+
if (raw && typeof raw.dataDir === 'string' && raw.dataDir.trim()) {
|
|
20
|
+
return path.resolve(raw.dataDir);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
/* 配置不可读时回落默认目录 */
|
|
25
|
+
}
|
|
26
|
+
return CONFIG_DIR();
|
|
27
|
+
}
|
|
28
|
+
/** resolveDataDir 的别名,语义化表达"数据目录"(与 CONFIG_DIR 配置目录区分) */
|
|
29
|
+
export const DATA_DIR = resolveDataDir;
|
|
7
30
|
/**
|
|
8
31
|
* 已知废弃的 URL 路径前缀(按部署形态演进顺序追加)。
|
|
9
32
|
* 每当 nginx / 服务端路径收敛(如子路径 → 根路径)时,把旧前缀加到这里,
|
|
@@ -70,6 +93,10 @@ export function ensureConfigDir() {
|
|
|
70
93
|
if (!fs.existsSync(CONFIG_DIR())) {
|
|
71
94
|
fs.mkdirSync(CONFIG_DIR(), { recursive: true });
|
|
72
95
|
}
|
|
96
|
+
const dataDir = resolveDataDir();
|
|
97
|
+
if (dataDir !== CONFIG_DIR() && !fs.existsSync(dataDir)) {
|
|
98
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
99
|
+
}
|
|
73
100
|
}
|
|
74
101
|
export function loadConfig() {
|
|
75
102
|
const raw = store.load();
|
|
@@ -59,5 +59,21 @@
|
|
|
59
59
|
"purge.done.next": "To re-initialize, run",
|
|
60
60
|
"purge.done.nothing": "Nothing to purge",
|
|
61
61
|
"login.serverOfflineSaasFallback": "Warning: local server is not running; logged in via SaaS directly. Start the server and run login again for local data operations.",
|
|
62
|
-
"login.serverOfflineReloginHint": "Note: the saved SaaS token will be rejected (401) by local server protected routes."
|
|
62
|
+
"login.serverOfflineReloginHint": "Note: the saved SaaS token will be rejected (401) by local server protected routes.",
|
|
63
|
+
"login.selfCheckOk": "Login self-check passed: protected endpoint reachable",
|
|
64
|
+
"login.selfCheckFail": "Login self-check failed: new token rejected by local server; please report with full output",
|
|
65
|
+
"login.selfCheckSkipped": "Login self-check skipped: local server unreachable; re-login once it is back",
|
|
66
|
+
"issueEnv.description": "Print the environment block needed for issue reports (CLI version / install type / git HEAD / server buildTime / OS); --format json for agents to paste directly",
|
|
67
|
+
"issueEnv.cliVersion": "CLI version",
|
|
68
|
+
"issueEnv.installType": "Install type",
|
|
69
|
+
"issueEnv.installDev": "dev (git worktree of source repo)",
|
|
70
|
+
"issueEnv.installPackage": "package (published npm build)",
|
|
71
|
+
"issueEnv.gitHead": "Code HEAD (version/buildTime do not identify code state; HEAD does)",
|
|
72
|
+
"issueEnv.gitBranch": "Branch",
|
|
73
|
+
"issueEnv.gitDirty": "uncommitted changes (dist may lag HEAD)",
|
|
74
|
+
"issueEnv.gitClean": "clean worktree",
|
|
75
|
+
"issueEnv.repoRoot": "Repo root",
|
|
76
|
+
"issueEnv.gitUnavailable": "unavailable (published package has no git info; use CLI version instead)",
|
|
77
|
+
"issueEnv.serverBuildTime": "server buildTime",
|
|
78
|
+
"issueEnv.serverNotRunning": "server not running (unavailable)"
|
|
63
79
|
}
|