hunter-mate 0.1.0 → 0.1.2

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.
@@ -4,9 +4,5 @@ export interface ConfigOptions {
4
4
  watchFolder?: string;
5
5
  lang?: string;
6
6
  }
7
- export declare function runConfig(options: ConfigOptions): {
8
- serverUrl: string;
9
- lang: string;
10
- watchFolder?: string;
11
- };
7
+ export declare function runConfig(options: ConfigOptions): import("../config").HunterMateConfig;
12
8
  export declare const configCommand: Command;
@@ -38,14 +38,44 @@ async function runLogin() {
38
38
  }
39
39
  exports.loginCommand = new commander_1.Command('login')
40
40
  .description(i18n_1.i18n.t('login.description'))
41
- .action(async () => {
41
+ .option('--email <email>', 'Email address (email verification code login)')
42
+ .option('--code <code>', 'Email verification code (6 digits, step 2)')
43
+ .action(async (options) => {
42
44
  try {
45
+ const config = (0, config_1.loadConfig)();
46
+ // 邮箱验证码登录(两步式无状态)
47
+ if (options.email) {
48
+ if (!options.code) {
49
+ // 第一步:发送验证码
50
+ await axios_1.default.post(`${config.serverUrl}/auth/email/send-code`, {
51
+ email: options.email,
52
+ });
53
+ console.log(`${i18n_1.i18n.t('login.emailCodeSent')} ${options.email}`);
54
+ console.log(` ${i18n_1.i18n.t('login.emailCodeNextStep')}`);
55
+ return;
56
+ }
57
+ // 第二步:校验验证码 → 保存 token
58
+ const { data } = await axios_1.default.post(`${config.serverUrl}/auth/email/verify`, { email: options.email, code: options.code });
59
+ if (!data.access_token) {
60
+ throw new Error('verify returned no access_token');
61
+ }
62
+ await (0, keychain_1.saveToken)(data.access_token);
63
+ console.log(i18n_1.i18n.t('login.success'));
64
+ return;
65
+ }
66
+ if (options.code) {
67
+ console.error(i18n_1.i18n.t('login.emailCodeMissing'));
68
+ process.exit(1);
69
+ }
70
+ // 默认:OAuth 设备码登录
43
71
  console.log(i18n_1.i18n.t('login.start'));
44
72
  await runLogin();
45
73
  console.log(i18n_1.i18n.t('login.success'));
46
74
  }
47
75
  catch (err) {
48
- console.error(i18n_1.i18n.t('login.failed'), err.message);
76
+ // 透出 server 侧业务错误信息(如"验证码错误"/"发送过于频繁")
77
+ const serverMsg = err?.response?.data?.message;
78
+ console.error(i18n_1.i18n.t('login.failed'), serverMsg || err.message);
49
79
  process.exit(1);
50
80
  }
51
81
  });
package/dist/config.d.ts CHANGED
@@ -5,8 +5,4 @@ export interface HunterMateConfig {
5
5
  }
6
6
  export declare function ensureConfigDir(): void;
7
7
  export declare function loadConfig(): HunterMateConfig;
8
- export declare function saveConfig(config: Partial<HunterMateConfig>): {
9
- serverUrl: string;
10
- lang: string;
11
- watchFolder?: string;
12
- };
8
+ export declare function saveConfig(config: Partial<HunterMateConfig>): HunterMateConfig;
package/dist/config.js CHANGED
@@ -38,28 +38,22 @@ exports.loadConfig = loadConfig;
38
38
  exports.saveConfig = saveConfig;
39
39
  const fs = __importStar(require("fs"));
40
40
  const path = __importStar(require("path"));
41
- const HOME = process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
42
- const CONFIG_DIR = path.join(HOME, '.hunter-mate');
43
- const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
41
+ const kit_1 = require("@chromepilot/kit");
42
+ const HOME = () => process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
43
+ const CONFIG_DIR = () => path.join(HOME(), '.hunter-mate');
44
+ const CONFIG_FILE = () => path.join(CONFIG_DIR(), 'config.json');
45
+ const store = (0, kit_1.createJsonConfigStore)({
46
+ configPath: CONFIG_FILE,
47
+ defaults: { serverUrl: 'http://localhost:34567', lang: 'zh-CN' },
48
+ });
44
49
  function ensureConfigDir() {
45
- if (!fs.existsSync(CONFIG_DIR)) {
46
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
50
+ if (!fs.existsSync(CONFIG_DIR())) {
51
+ fs.mkdirSync(CONFIG_DIR(), { recursive: true });
47
52
  }
48
53
  }
49
54
  function loadConfig() {
50
- ensureConfigDir();
51
- if (fs.existsSync(CONFIG_FILE)) {
52
- return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
53
- }
54
- return {
55
- serverUrl: 'http://localhost:34567',
56
- lang: 'zh-CN',
57
- };
55
+ return store.load();
58
56
  }
59
57
  function saveConfig(config) {
60
- ensureConfigDir();
61
- const current = loadConfig();
62
- const next = { ...current, ...config };
63
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(next, null, 2));
64
- return next;
58
+ return store.save(config);
65
59
  }
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "cli.description": "HiredChina headhunter Agent data assistant",
3
3
  "cli.langOption": "Output language (zh-CN/en-US)",
4
- "login.description": "Login with OAuth device code",
4
+ "login.description": "Login: OAuth device code by default; use --email for email verification code (two steps: send code first, then verify with --code)",
5
5
  "login.start": "Opening browser to complete login...",
6
6
  "login.userCode": "User code:",
7
7
  "login.verifyUrl": "Verification URL:",
8
8
  "login.success": "Login successful",
9
9
  "login.failed": "Login failed:",
10
10
  "login.expired": "Device code expired, please try again",
11
+ "login.emailCodeSent": "Verification code sent to your email (valid for 10 minutes). Continue with step 2:",
12
+ "login.emailCodeNextStep": "hunter-mate login --email <email> --code <6-digit code>",
13
+ "login.emailCodeMissing": "Missing --code. Step 2 usage: hunter-mate login --email <email> --code <6-digit code>",
11
14
  "logout.description": "Logout and delete local token",
12
15
  "logout.success": "Logged out",
13
16
  "status.description": "Check service, login, and extension status",
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "cli.description": "HiredChina 猎头 Agent 数据助手",
3
3
  "cli.langOption": "输出语言(zh-CN/en-US)",
4
- "login.description": "使用 OAuth 设备码登录",
4
+ "login.description": "登录:默认 OAuth 设备码;--email 走邮箱验证码(两步:先发码,后带 --code 校验)",
5
5
  "login.start": "正在打开浏览器完成登录...",
6
6
  "login.userCode": "用户码:",
7
7
  "login.verifyUrl": "授权地址:",
8
8
  "login.success": "登录成功",
9
9
  "login.failed": "登录失败:",
10
10
  "login.expired": "设备码已过期,请重试",
11
+ "login.emailCodeSent": "验证码已发送到邮箱(10 分钟内有效)。请查收后执行第二步:",
12
+ "login.emailCodeNextStep": "hunter-mate login --email <邮箱> --code <6位验证码>",
13
+ "login.emailCodeMissing": "缺少 --code。第二步用法:hunter-mate login --email <邮箱> --code <6位验证码>",
11
14
  "logout.description": "退出登录并删除本地 token",
12
15
  "logout.success": "已退出登录",
13
16
  "status.description": "查看服务、登录、扩展状态",
package/dist/index.js CHANGED
@@ -61,12 +61,14 @@ const extras_1 = require("./commands/extras");
61
61
  const ops_1 = require("./commands/ops");
62
62
  const addons_1 = require("./commands/addons");
63
63
  const i18n_1 = require("./i18n");
64
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
65
+ const { version: CLI_VERSION } = require('../package.json');
64
66
  (0, sentry_1.initSentry)();
65
67
  const program = new commander_1.Command();
66
68
  program
67
69
  .name('hunter-mate')
68
70
  .description(i18n_1.i18n.t('cli.description') + '(已对齐 ChromePilot: init/install/start/stop/status/recordings/control/adapter/run/memory/trace/autofix/external/plugin/skill/open-ext/doctor/reload)')
69
- .version('0.1.0')
71
+ .version(CLI_VERSION)
70
72
  .option('--lang <lang>', i18n_1.i18n.t('cli.langOption'), 'zh-CN')
71
73
  .hook('preAction', async (thisCommand) => {
72
74
  const lang = thisCommand.opts().lang;
package/dist/sentry.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- import * as Sentry from '@sentry/node';
2
- export declare function initSentry(): void;
1
+ import { Sentry } from '@chromepilot/kit';
2
+ /** Sentry 初始化:通用骨架来自 @chromepilot/kit(HM_ 前缀 env 读取) */
3
+ export declare function initSentry(): boolean;
3
4
  export { Sentry };
package/dist/sentry.js CHANGED
@@ -1,62 +1,10 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.Sentry = void 0;
37
4
  exports.initSentry = initSentry;
38
- const Sentry = __importStar(require("@sentry/node"));
39
- exports.Sentry = Sentry;
5
+ const kit_1 = require("@chromepilot/kit");
6
+ Object.defineProperty(exports, "Sentry", { enumerable: true, get: function () { return kit_1.Sentry; } });
7
+ /** Sentry 初始化:通用骨架来自 @chromepilot/kit(HM_ 前缀 env 读取) */
40
8
  function initSentry() {
41
- const dsn = process.env.HM_ERROR_TRACKING_DSN;
42
- const enabled = process.env.HM_ERROR_TRACKING_ENABLED === 'true';
43
- if (!enabled || !dsn)
44
- return;
45
- Sentry.init({
46
- dsn,
47
- release: process.env.HM_RELEASE,
48
- environment: process.env.HM_ENVIRONMENT || 'development',
49
- tracesSampleRate: 0,
50
- sendClientReports: false,
51
- beforeSend(event) {
52
- if (event.request?.headers) {
53
- const headers = event.request.headers;
54
- if (headers.authorization)
55
- headers.authorization = '***';
56
- if (headers.cookie)
57
- headers.cookie = '***';
58
- }
59
- return event;
60
- },
61
- });
9
+ return (0, kit_1.initSentryFromEnv)('HM');
62
10
  }
package/dist/updater.d.ts CHANGED
@@ -5,19 +5,18 @@
5
5
  */
6
6
  export declare function computeDevLinked(pkgRoot: string): boolean;
7
7
  export declare function isDevLinked(): boolean;
8
- /** npm registry 查询最新版本(spawnSync,不占用 axios mock 队列);失败返回 null */
9
- export declare function npmLatestVersion(pkg?: string): string | null;
10
- /** 自更新: npm install -g <pkg>@latest(运行中的进程无法替换,新命令生效) */
11
- export declare function npmSelfUpdate(pkg?: string): {
8
+ export declare const npmLatestVersion: (pkg?: string) => string | null;
9
+ export declare const npmSelfUpdate: (pkg?: string) => {
12
10
  ok: boolean;
13
11
  msg: string;
14
12
  };
15
- export declare function updateCheckIntervalMs(): number;
16
- export declare function shouldCheckUpdate(stateFile?: string, now?: number, intervalMs?: number): boolean;
17
- export declare function markUpdateChecked(stateFile?: string): void;
13
+ export declare const updateCheckIntervalMs: () => number;
14
+ export declare const shouldCheckUpdate: (stateFile?: string, now?: number, intervalMs?: number) => boolean;
15
+ export declare const markUpdateChecked: (stateFile?: string) => void;
18
16
  export interface CliUpdateOutcome {
19
17
  logs: string[];
20
18
  devSkipped: boolean;
21
19
  updated: boolean;
22
20
  }
21
+ /** preAction 集成入口:返回日志行数组,永不 throw */
23
22
  export declare function maybeCliSelfUpdate(): Promise<CliUpdateOutcome>;
package/dist/updater.js CHANGED
@@ -33,36 +33,28 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.markUpdateChecked = exports.shouldCheckUpdate = exports.updateCheckIntervalMs = exports.npmSelfUpdate = exports.npmLatestVersion = void 0;
36
37
  exports.computeDevLinked = computeDevLinked;
37
38
  exports.isDevLinked = isDevLinked;
38
- exports.npmLatestVersion = npmLatestVersion;
39
- exports.npmSelfUpdate = npmSelfUpdate;
40
- exports.updateCheckIntervalMs = updateCheckIntervalMs;
41
- exports.shouldCheckUpdate = shouldCheckUpdate;
42
- exports.markUpdateChecked = markUpdateChecked;
43
39
  exports.maybeCliSelfUpdate = maybeCliSelfUpdate;
44
40
  /**
45
- * CLI 自动更新(使用态)与开发模式识别(与 chromepilot updater 对齐)。
41
+ * CLI 自动更新(使用态)— 通用实现来自 @chromepilot/kit createCliUpdater。
46
42
  *
47
- * 设计(ADR-005 延伸):
48
- * - 每次命令使用时(preAction)节流(默认 1h)对比 npm registry 最新版本,落后则自动
49
- * `npm install -g hunter-mate@latest`;运行中的进程无法替换自身,下一条命令起生效。
50
- * - 开发软链模式(wrapper 指向 hc-hw local worktree dist)跳过 CLI 自更新,由开发者
51
- * `pnpm build` 控制;server/插件/扩展的一致性 reload/restart 不受影响(version.ts)。
52
- * - 逃生开关:
53
- * - HUNTER_MATE_NO_AUTOUPDATE=1 关闭全部自动更新(CLI + 一致性,preAction 层)
54
- * - HUNTER_MATE_UPDATE_CHECK_INTERVAL_MS 覆盖节流间隔(默认 3600000)
55
- * - HUNTER_MATE_DEV=1 强制视为 dev 模式(测试用)
43
+ * hunter-mate 差异点通过工厂参数注入:
44
+ * - npm 包名 hunter-mate、env 前缀 HUNTER_MATE、状态目录 ~/.hunter-mate
45
+ * - dev 判定(hc-hw local worktree 布局 + HUNTER_MATE_DEV=1)为本仓特有安装形态
46
+ * - 已安装版本读取走 version.ts 的 package.json SSOT 扫描
47
+ *
48
+ * 逃生开关(kit autoCliUpdate 内统一处理):
49
+ * - HUNTER_MATE_NO_AUTOUPDATE=1 关闭全部自动更新(CLI + 一致性,preAction 层)
50
+ * - HUNTER_MATE_UPDATE_CHECK_INTERVAL_MS 覆盖节流间隔(默认 3600000)
51
+ * - HUNTER_MATE_DEV=1 强制视为 dev 模式(测试用)
56
52
  */
57
- const fs = __importStar(require("fs"));
58
53
  const path = __importStar(require("path"));
59
- const child_process_1 = require("child_process");
54
+ const kit_1 = require("@chromepilot/kit");
60
55
  const version_1 = require("./version");
61
56
  const HOME = () => process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
62
57
  const ROOT = () => path.join(HOME(), '.hunter-mate');
63
- const LAST_CHECK_FILE = () => path.join(ROOT(), '.last-cli-update-check');
64
- const NPM_PACKAGE = 'hunter-mate';
65
- const DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
66
58
  /**
67
59
  * 判定是否开发软链模式(纯函数,便于单测):
68
60
  * 包根路径含 /worktrees/ 段(hc-hw local worktree 布局)或 HUNTER_MATE_DEV=1。
@@ -76,86 +68,22 @@ function computeDevLinked(pkgRoot) {
76
68
  function isDevLinked() {
77
69
  return computeDevLinked(path.join(__dirname, '..'));
78
70
  }
79
- /** npm registry 查询最新版本(spawnSync,不占用 axios mock 队列);失败返回 null */
80
- function npmLatestVersion(pkg = NPM_PACKAGE) {
81
- try {
82
- const r = (0, child_process_1.spawnSync)('npm', ['view', pkg, 'version'], {
83
- encoding: 'utf-8',
84
- timeout: 15_000,
85
- });
86
- if (r.status === 0 && r.stdout)
87
- return r.stdout.trim() || null;
88
- return null;
89
- }
90
- catch {
91
- return null;
92
- }
93
- }
94
- /** 自更新: npm install -g <pkg>@latest(运行中的进程无法替换,新命令生效) */
95
- function npmSelfUpdate(pkg = NPM_PACKAGE) {
96
- const r = (0, child_process_1.spawnSync)('npm', ['install', '-g', `${pkg}@latest`], {
97
- stdio: 'inherit',
98
- timeout: 120_000,
99
- });
100
- if (r.status === 0) {
101
- return { ok: true, msg: `npm install -g ${pkg}@latest 完成,下一条命令起使用新版本` };
102
- }
103
- return { ok: false, msg: `npm install -g ${pkg}@latest 失败 (exit=${r.status}),可手动执行` };
104
- }
71
+ const updater = (0, kit_1.createCliUpdater)({
72
+ npmPackage: 'hunter-mate',
73
+ envPrefix: 'HUNTER_MATE',
74
+ stateDir: ROOT,
75
+ getInstalledVersion: () => (0, version_1.readInstalledVersions)().cli,
76
+ isDev: isDevLinked,
77
+ });
105
78
  // ---------------------------------------------------------------------------
106
- // 节流
79
+ // 兼容导出(调用方零改动)
107
80
  // ---------------------------------------------------------------------------
108
- function updateCheckIntervalMs() {
109
- const v = Number(process.env.HUNTER_MATE_UPDATE_CHECK_INTERVAL_MS);
110
- return Number.isFinite(v) && v > 0 ? v : DEFAULT_INTERVAL_MS;
111
- }
112
- function shouldCheckUpdate(stateFile = LAST_CHECK_FILE(), now = Date.now(), intervalMs = updateCheckIntervalMs()) {
113
- try {
114
- const stat = fs.statSync(stateFile);
115
- return now - stat.mtimeMs > intervalMs;
116
- }
117
- catch {
118
- return true;
119
- }
120
- }
121
- function markUpdateChecked(stateFile = LAST_CHECK_FILE()) {
122
- try {
123
- fs.mkdirSync(path.dirname(stateFile), { recursive: true });
124
- fs.writeFileSync(stateFile, String(Date.now()));
125
- }
126
- catch {
127
- /* 尽力 */
128
- }
129
- }
81
+ exports.npmLatestVersion = updater.npmLatestVersion;
82
+ exports.npmSelfUpdate = updater.npmSelfUpdate;
83
+ exports.updateCheckIntervalMs = updater.updateCheckIntervalMs;
84
+ exports.shouldCheckUpdate = updater.shouldCheckUpdate;
85
+ exports.markUpdateChecked = updater.markUpdateChecked;
86
+ /** preAction 集成入口:返回日志行数组,永不 throw */
130
87
  async function maybeCliSelfUpdate() {
131
- const logs = [];
132
- const outcome = { logs, devSkipped: false, updated: false };
133
- try {
134
- if (isDevLinked()) {
135
- outcome.devSkipped = true;
136
- logs.push('dev 软链模式,跳过 CLI 自更新(改代码请 pnpm build;一致性 reload/restart 不受影响)');
137
- return outcome;
138
- }
139
- if (!shouldCheckUpdate())
140
- return outcome;
141
- markUpdateChecked();
142
- const installed = (0, version_1.readInstalledVersions)().cli;
143
- if (!installed || installed === '0.0.0')
144
- return outcome;
145
- const latest = npmLatestVersion();
146
- if (!latest) {
147
- logs.push(`npm registry 查询 ${NPM_PACKAGE} 失败,本次跳过自更新`);
148
- return outcome;
149
- }
150
- if (latest === installed)
151
- return outcome;
152
- logs.push(`CLI 自更新: ${installed} → ${latest}`);
153
- const r = npmSelfUpdate();
154
- logs.push(r.msg);
155
- outcome.updated = r.ok;
156
- }
157
- catch (err) {
158
- logs.push(`自动更新检测异常(不影响命令执行): ${err instanceof Error ? err.message : String(err)}`);
159
- }
160
- return outcome;
88
+ return updater.autoCliUpdate();
161
89
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hunter-mate",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "HiredChina 猎头 Agent 数据助手 CLI:人才库/客户/职位数据访问,浏览器录制解析简历,server/扩展一致性自愈",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -18,6 +18,7 @@
18
18
  "dist"
19
19
  ],
20
20
  "dependencies": {
21
+ "@chromepilot/kit": "^0.1.1",
21
22
  "@sentry/node": "^10.70.0",
22
23
  "@types/ws": "^8.18.1",
23
24
  "axios": "^1.6.0",
@@ -29,7 +30,7 @@
29
30
  "open": "^8.4.0",
30
31
  "uuid": "^9.0.0",
31
32
  "ws": "^8.21.3",
32
- "@hunter-mate/shared": "0.1.0"
33
+ "@hunter-mate/shared": "0.1.2"
33
34
  },
34
35
  "devDependencies": {
35
36
  "@types/better-sqlite3": "^7.6.0",