hunter-mate 0.1.6 → 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.
Files changed (261) hide show
  1. package/dist/assets/extension/background/service-worker.js +911 -159
  2. package/dist/assets/extension/build-info.json +1 -0
  3. package/dist/assets/extension/content/index.js +22 -4
  4. package/dist/assets/extension/manifest.json +5 -4
  5. package/dist/assets/extension/offscreen/offscreen.html +8 -0
  6. package/dist/assets/extension/offscreen/offscreen.js +149 -0
  7. package/dist/assets/extension/package.json +12 -4
  8. package/dist/assets/extension/sidepanel/index.css +2 -0
  9. package/dist/assets/extension/sidepanel/index.html +7 -394
  10. package/dist/assets/extension/sidepanel/index.js +53 -923
  11. package/dist/assets/server/app.module.js +12 -1
  12. package/dist/assets/server/app.module.js.map +1 -1
  13. package/dist/assets/server/auth/auth.controller.d.ts +7 -0
  14. package/dist/assets/server/auth/auth.controller.js +20 -0
  15. package/dist/assets/server/auth/auth.controller.js.map +1 -1
  16. package/dist/assets/server/auth/auth.middleware.js +9 -1
  17. package/dist/assets/server/auth/auth.middleware.js.map +1 -1
  18. package/dist/assets/server/auth/auth.service.d.ts +11 -0
  19. package/dist/assets/server/auth/auth.service.js +58 -1
  20. package/dist/assets/server/auth/auth.service.js.map +1 -1
  21. package/dist/assets/server/build-info.json +1 -1
  22. package/dist/assets/server/customer/customer.controller.d.ts +18 -0
  23. package/dist/assets/server/customer/customer.controller.js +81 -0
  24. package/dist/assets/server/customer/customer.controller.js.map +1 -0
  25. package/dist/assets/server/customer/customer.entity.d.ts +17 -0
  26. package/dist/assets/server/customer/customer.entity.js +82 -0
  27. package/dist/assets/server/customer/customer.entity.js.map +1 -0
  28. package/dist/assets/server/customer/customer.module.d.ts +2 -0
  29. package/dist/assets/server/customer/customer.module.js +28 -0
  30. package/dist/assets/server/customer/customer.module.js.map +1 -0
  31. package/dist/assets/server/customer/customer.service.d.ts +36 -0
  32. package/dist/assets/server/customer/customer.service.js +200 -0
  33. package/dist/assets/server/customer/customer.service.js.map +1 -0
  34. package/dist/assets/server/db/schema-validator.service.d.ts +2 -0
  35. package/dist/assets/server/db/schema-validator.service.js +32 -1
  36. package/dist/assets/server/db/schema-validator.service.js.map +1 -1
  37. package/dist/assets/server/db/sqlite.module.d.ts +1 -0
  38. package/dist/assets/server/db/sqlite.module.js +12 -2
  39. package/dist/assets/server/db/sqlite.module.js.map +1 -1
  40. package/dist/assets/server/devops/devops-sink-init.d.ts +1 -14
  41. package/dist/assets/server/devops/devops-sink-init.js +3 -8
  42. package/dist/assets/server/devops/devops-sink-init.js.map +1 -1
  43. package/dist/assets/server/extension/extension-control.controller.d.ts +15 -0
  44. package/dist/assets/server/extension/extension-control.controller.js +42 -2
  45. package/dist/assets/server/extension/extension-control.controller.js.map +1 -1
  46. package/dist/assets/server/extension/extension-control.service.d.ts +14 -1
  47. package/dist/assets/server/extension/extension-control.service.js +65 -2
  48. package/dist/assets/server/extension/extension-control.service.js.map +1 -1
  49. package/dist/assets/server/extraction/adapter.service.d.ts +3 -0
  50. package/dist/assets/server/extraction/adapter.service.js +127 -6
  51. package/dist/assets/server/extraction/adapter.service.js.map +1 -1
  52. package/dist/assets/server/extraction/anomaly-report.service.js +1 -1
  53. package/dist/assets/server/extraction/anomaly-report.service.js.map +1 -1
  54. package/dist/assets/server/extraction/entities/adapter-rule.entity.d.ts +2 -0
  55. package/dist/assets/server/extraction/entities/adapter-rule.entity.js +10 -0
  56. package/dist/assets/server/extraction/entities/adapter-rule.entity.js.map +1 -1
  57. package/dist/assets/server/extraction/extraction-entity.controller.d.ts +21 -3
  58. package/dist/assets/server/extraction/extraction-entity.controller.js +44 -9
  59. package/dist/assets/server/extraction/extraction-entity.controller.js.map +1 -1
  60. package/dist/assets/server/extraction/extraction.module.js +13 -3
  61. package/dist/assets/server/extraction/extraction.module.js.map +1 -1
  62. package/dist/assets/server/extraction/extraction.service.d.ts +4 -1
  63. package/dist/assets/server/extraction/extraction.service.js +59 -4
  64. package/dist/assets/server/extraction/extraction.service.js.map +1 -1
  65. package/dist/assets/server/extraction/harness-overlay.service.d.ts +41 -0
  66. package/dist/assets/server/extraction/harness-overlay.service.js +319 -0
  67. package/dist/assets/server/extraction/harness-overlay.service.js.map +1 -0
  68. package/dist/assets/server/extraction/liepin-extractor.d.ts +1 -0
  69. package/dist/assets/server/extraction/liepin-extractor.js +172 -0
  70. package/dist/assets/server/extraction/liepin-extractor.js.map +1 -0
  71. package/dist/assets/server/extraction/script-rule.js +4 -0
  72. package/dist/assets/server/extraction/script-rule.js.map +1 -1
  73. package/dist/assets/server/governance/action-audit.entity.d.ts +12 -0
  74. package/dist/assets/server/governance/action-audit.entity.js +66 -0
  75. package/dist/assets/server/governance/action-audit.entity.js.map +1 -0
  76. package/dist/assets/server/governance/governance.controller.d.ts +14 -0
  77. package/dist/assets/server/governance/governance.controller.js +77 -0
  78. package/dist/assets/server/governance/governance.controller.js.map +1 -0
  79. package/dist/assets/server/governance/governance.ingester.d.ts +20 -0
  80. package/dist/assets/server/governance/governance.ingester.js +114 -0
  81. package/dist/assets/server/governance/governance.ingester.js.map +1 -0
  82. package/dist/assets/server/governance/governance.module.d.ts +2 -0
  83. package/dist/assets/server/governance/governance.module.js +26 -0
  84. package/dist/assets/server/governance/governance.module.js.map +1 -0
  85. package/dist/assets/server/governance/governance.service.d.ts +114 -0
  86. package/dist/assets/server/governance/governance.service.js +356 -0
  87. package/dist/assets/server/governance/governance.service.js.map +1 -0
  88. package/dist/assets/server/jobcase/jobcase.controller.d.ts +1 -0
  89. package/dist/assets/server/jobcase/jobcase.entity.d.ts +1 -0
  90. package/dist/assets/server/jobcase/jobcase.entity.js +6 -0
  91. package/dist/assets/server/jobcase/jobcase.entity.js.map +1 -1
  92. package/dist/assets/server/jobcase/jobcase.service.d.ts +2 -0
  93. package/dist/assets/server/jobcase/jobcase.service.js +2 -1
  94. package/dist/assets/server/jobcase/jobcase.service.js.map +1 -1
  95. package/dist/assets/server/main.js +1 -0
  96. package/dist/assets/server/main.js.map +1 -1
  97. package/dist/assets/server/package.json +5 -4
  98. package/dist/assets/server/preference/preference.controller.d.ts +14 -0
  99. package/dist/assets/server/preference/preference.controller.js +66 -0
  100. package/dist/assets/server/preference/preference.controller.js.map +1 -0
  101. package/dist/assets/server/preference/preference.entity.d.ts +13 -0
  102. package/dist/assets/server/preference/preference.entity.js +77 -0
  103. package/dist/assets/server/preference/preference.entity.js.map +1 -0
  104. package/dist/assets/server/preference/preference.module.d.ts +2 -0
  105. package/dist/assets/server/preference/preference.module.js +26 -0
  106. package/dist/assets/server/preference/preference.module.js.map +1 -0
  107. package/dist/assets/server/preference/preference.service.d.ts +33 -0
  108. package/dist/assets/server/preference/preference.service.js +122 -0
  109. package/dist/assets/server/preference/preference.service.js.map +1 -0
  110. package/dist/assets/server/recording/recording-event-queue.service.d.ts +26 -0
  111. package/dist/assets/server/recording/recording-event-queue.service.js +115 -0
  112. package/dist/assets/server/recording/recording-event-queue.service.js.map +1 -0
  113. package/dist/assets/server/recording/recording-retention.service.d.ts +38 -0
  114. package/dist/assets/server/recording/recording-retention.service.js +174 -0
  115. package/dist/assets/server/recording/recording-retention.service.js.map +1 -0
  116. package/dist/assets/server/recording/recording-snapshot.service.d.ts +1 -0
  117. package/dist/assets/server/recording/recording-snapshot.service.js +4 -0
  118. package/dist/assets/server/recording/recording-snapshot.service.js.map +1 -1
  119. package/dist/assets/server/recording/recording.controller.d.ts +21 -1
  120. package/dist/assets/server/recording/recording.controller.js +37 -2
  121. package/dist/assets/server/recording/recording.controller.js.map +1 -1
  122. package/dist/assets/server/recording/recording.gateway.js +11 -0
  123. package/dist/assets/server/recording/recording.gateway.js.map +1 -1
  124. package/dist/assets/server/recording/recording.module.js +16 -2
  125. package/dist/assets/server/recording/recording.module.js.map +1 -1
  126. package/dist/assets/server/recording/recording.service.d.ts +10 -1
  127. package/dist/assets/server/recording/recording.service.js +31 -6
  128. package/dist/assets/server/recording/recording.service.js.map +1 -1
  129. package/dist/assets/server/remark/remark.controller.d.ts +1 -1
  130. package/dist/assets/server/remark/remark.controller.js +1 -10
  131. package/dist/assets/server/remark/remark.controller.js.map +1 -1
  132. package/dist/assets/server/remark/remark.entity.d.ts +1 -1
  133. package/dist/assets/server/remark/remark.entity.js.map +1 -1
  134. package/dist/assets/server/site/entities/site-registry.entity.d.ts +1 -1
  135. package/dist/assets/server/site/entities/site-registry.entity.js +1 -1
  136. package/dist/assets/server/site/entities/site-registry.entity.js.map +1 -1
  137. package/dist/assets/server/site/site.controller.d.ts +1 -1
  138. package/dist/assets/server/site/site.service.d.ts +5 -1
  139. package/dist/assets/server/site/site.service.js +13 -0
  140. package/dist/assets/server/site/site.service.js.map +1 -1
  141. package/dist/assets/server/system/system.controller.d.ts +1 -0
  142. package/dist/assets/server/system/system.controller.js +1 -1
  143. package/dist/assets/server/system/system.controller.js.map +1 -1
  144. package/dist/assets/server/system/system.service.d.ts +1 -0
  145. package/dist/assets/server/system/system.service.js +3 -2
  146. package/dist/assets/server/system/system.service.js.map +1 -1
  147. package/dist/cache/db.js +6 -6
  148. package/dist/commands/addons.js +2 -1
  149. package/dist/commands/cache.js +3 -4
  150. package/dist/commands/case.d.ts +67 -0
  151. package/dist/commands/case.js +101 -0
  152. package/dist/commands/config.d.ts +4 -0
  153. package/dist/commands/config.js +8 -1
  154. package/dist/commands/customer.d.ts +25 -0
  155. package/dist/commands/customer.js +137 -0
  156. package/dist/commands/doctor.js +119 -13
  157. package/dist/commands/extension.js +26 -32
  158. package/dist/commands/extras.d.ts +22 -3
  159. package/dist/commands/extras.js +116 -7
  160. package/dist/commands/init.d.ts +1 -1
  161. package/dist/commands/init.js +3 -4
  162. package/dist/commands/issue-env.d.ts +23 -0
  163. package/dist/commands/issue-env.js +101 -0
  164. package/dist/commands/job.d.ts +6 -2
  165. package/dist/commands/job.js +75 -20
  166. package/dist/commands/login.d.ts +19 -0
  167. package/dist/commands/login.js +85 -13
  168. package/dist/commands/migrate.d.ts +32 -0
  169. package/dist/commands/migrate.js +116 -0
  170. package/dist/commands/note.d.ts +3 -0
  171. package/dist/commands/note.js +75 -0
  172. package/dist/commands/ops.js +82 -15
  173. package/dist/commands/preference.d.ts +6 -0
  174. package/dist/commands/preference.js +80 -0
  175. package/dist/commands/record.js +2 -4
  176. package/dist/commands/report.js +2 -4
  177. package/dist/commands/search.js +9 -2
  178. package/dist/commands/server.d.ts +9 -2
  179. package/dist/commands/server.js +118 -19
  180. package/dist/commands/status.d.ts +4 -0
  181. package/dist/commands/status.js +16 -6
  182. package/dist/commands/update.js +8 -9
  183. package/dist/config.d.ts +21 -0
  184. package/dist/config.js +27 -0
  185. package/dist/i18n/locales/en-US.json +19 -1
  186. package/dist/i18n/locales/zh-CN.json +19 -1
  187. package/dist/index.js +52 -2
  188. package/dist/keychain.d.ts +16 -1
  189. package/dist/keychain.js +43 -16
  190. package/dist/logger.js +2 -2
  191. package/dist/updater.d.ts +1 -1
  192. package/dist/updater.js +24 -7
  193. package/dist/utils/command-error.d.ts +11 -0
  194. package/dist/utils/command-error.js +47 -0
  195. package/dist/utils/link.d.ts +18 -0
  196. package/dist/utils/link.js +83 -0
  197. package/dist/utils/proxy-detect.d.ts +20 -0
  198. package/dist/utils/proxy-detect.js +96 -0
  199. package/dist/utils/script-path.d.ts +5 -0
  200. package/dist/utils/script-path.js +27 -0
  201. package/dist/version.js +34 -9
  202. package/dist/watchdog.d.ts +23 -1
  203. package/dist/watchdog.js +97 -6
  204. package/dist/web/dist/404.html +1 -0
  205. package/dist/web/dist/_next/static/WdJr3NhHjlu5xN0qOap1k/_buildManifest.js +1 -0
  206. package/dist/web/dist/_next/static/WdJr3NhHjlu5xN0qOap1k/_ssgManifest.js +1 -0
  207. package/dist/web/dist/_next/static/chunks/166.f30be223f694f8ee.js +1 -0
  208. package/dist/web/dist/_next/static/chunks/1e6d5a0c.0488932e17ecdf24.js +15 -0
  209. package/dist/web/dist/_next/static/chunks/539-5d0e8c5c238c56f8.js +1 -0
  210. package/dist/web/dist/_next/static/chunks/589-d632233641f35681.js +1 -0
  211. package/dist/web/dist/_next/static/chunks/671.3b8e9cc96b25b6ea.js +1 -0
  212. package/dist/web/dist/_next/static/chunks/935-99f57b7d4151d9e7.js +5 -0
  213. package/dist/web/dist/_next/static/chunks/a7067448-96dd4b8457c447e8.js +1 -0
  214. package/dist/web/dist/_next/static/chunks/app/_not-found/page-fdf1a13a7656886a.js +1 -0
  215. package/dist/web/dist/_next/static/chunks/app/auth/verify/page-8e5cff53d6f7d8e1.js +1 -0
  216. package/dist/web/dist/_next/static/chunks/app/companies/page-e38a69e1929d0953.js +1 -0
  217. package/dist/web/dist/_next/static/chunks/app/entity/page-250f8a9b6d91ab19.js +1 -0
  218. package/dist/web/dist/_next/static/chunks/app/jobs/page-6a281776fec065f6.js +1 -0
  219. package/dist/web/dist/_next/static/chunks/app/layout-83e623e973c1693f.js +1 -0
  220. package/dist/web/dist/_next/static/chunks/app/logs/page-d478031d49f7ba00.js +1 -0
  221. package/dist/web/dist/_next/static/chunks/app/matching/jobs/[id]/talents/page-f57f4b7529823601.js +1 -0
  222. package/dist/web/dist/_next/static/chunks/app/matching/portraits/[id]/jobs/page-614d1e62d7b9d215.js +1 -0
  223. package/dist/web/dist/_next/static/chunks/app/page-e3392de08ae81fba.js +1 -0
  224. package/dist/web/dist/_next/static/chunks/app/parselog/page-58d3acece95b4089.js +1 -0
  225. package/dist/web/dist/_next/static/chunks/app/recordings/page-e2e2e3c3a64083fb.js +1 -0
  226. package/dist/web/dist/_next/static/chunks/app/snapshots/[id]/agent-prompt/page-fd3b7d7373b8f8b7.js +1 -0
  227. package/dist/web/dist/_next/static/chunks/app/talents/page-30748338da20d7e0.js +1 -0
  228. package/dist/web/dist/_next/static/chunks/framework-b3a558b17a070965.js +1 -0
  229. package/dist/web/dist/_next/static/chunks/main-5a4e0cd75dd7f04b.js +1 -0
  230. package/dist/web/dist/_next/static/chunks/main-app-80b038a312917d90.js +1 -0
  231. package/dist/web/dist/_next/static/chunks/pages/_app-da7d628f49271ece.js +4 -0
  232. package/dist/web/dist/_next/static/chunks/pages/_error-17756f67e0a11350.js +1 -0
  233. package/dist/web/dist/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  234. package/dist/web/dist/_next/static/chunks/webpack-1784baecc6fdcf0b.js +1 -0
  235. package/dist/web/dist/_next/static/css/db881c5b3072b88a.css +3 -0
  236. package/dist/web/dist/_next/static/css/f60d210841fab76b.css +1 -0
  237. package/dist/web/dist/auth/verify.html +1 -0
  238. package/dist/web/dist/auth/verify.txt +8 -0
  239. package/dist/web/dist/companies.html +1 -0
  240. package/dist/web/dist/companies.txt +7 -0
  241. package/dist/web/dist/entity.html +1 -0
  242. package/dist/web/dist/entity.txt +8 -0
  243. package/dist/web/dist/index.html +1 -0
  244. package/dist/web/dist/index.txt +8 -0
  245. package/dist/web/dist/jobs.html +1 -0
  246. package/dist/web/dist/jobs.txt +7 -0
  247. package/dist/web/dist/logs.html +1 -0
  248. package/dist/web/dist/logs.txt +8 -0
  249. package/dist/web/dist/matching/jobs/placeholder/talents.html +1 -0
  250. package/dist/web/dist/matching/jobs/placeholder/talents.txt +8 -0
  251. package/dist/web/dist/matching/portraits/placeholder/jobs.html +1 -0
  252. package/dist/web/dist/matching/portraits/placeholder/jobs.txt +8 -0
  253. package/dist/web/dist/parselog.html +1 -0
  254. package/dist/web/dist/parselog.txt +8 -0
  255. package/dist/web/dist/recordings.html +1 -0
  256. package/dist/web/dist/recordings.txt +8 -0
  257. package/dist/web/dist/snapshots/placeholder/agent-prompt.html +1 -0
  258. package/dist/web/dist/snapshots/placeholder/agent-prompt.txt +8 -0
  259. package/dist/web/dist/talents.html +1 -0
  260. package/dist/web/dist/talents.txt +7 -0
  261. package/package.json +26 -6
@@ -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',
@@ -67,6 +67,27 @@ export async function runDoctor(opts) {
67
67
  status: nodeVersionFriendly ? 'ok' : 'warn',
68
68
  detail: `Node ${process.versions.node}${nodeVersionFriendly ? '' : ' (hunter-mate 预构建包对 Node 20/22/24 支持最好,Windows 无编译器时建议切到 Node 22 LTS)'}`,
69
69
  });
70
+ // ── 0.5) 死代理检测(Windows 现场:npm registry 查询失败 / prebuild 下载失败
71
+ // 回退 7 分钟源码编译的根因是 shell 残留指向死端口的代理变量) ──
72
+ try {
73
+ const { findDeadProxies, buildUnsetHint } = await import('../utils/proxy-detect.js');
74
+ const dead = await findDeadProxies(800);
75
+ if (dead.length) {
76
+ checks.push({ key: 'proxy_env', status: 'warn', detail: buildUnsetHint(dead) });
77
+ hints.push('清除死代理环境变量后重试(见 proxy_env 检查项的各 shell 指令)');
78
+ }
79
+ else {
80
+ const proxyEnv = (await import('../utils/proxy-detect.js')).collectProxyEnv();
81
+ checks.push({
82
+ key: 'proxy_env',
83
+ status: 'ok',
84
+ detail: proxyEnv.length
85
+ ? `代理变量已设置且端口存活: ${proxyEnv.map((p) => `${p.varName}=${p.value}`).join(', ')}`
86
+ : '未设置代理环境变量(直连)',
87
+ });
88
+ }
89
+ }
90
+ catch { /* 检测失败不阻断 doctor */ }
70
91
  // ── 1) 目录 & token ──
71
92
  let initResult = null;
72
93
  try {
@@ -160,9 +181,17 @@ export async function runDoctor(opts) {
160
181
  }
161
182
  else {
162
183
  try {
163
- const r = await axios.get(`${config.serverUrl}/health`, { timeout: 3000 });
184
+ const r = await axios.get(`${config.serverUrl}/health`, { timeout: 3000, proxy: false });
164
185
  serverUp = true;
165
- checks.push({ key: 'server', status: 'ok', detail: `${config.serverUrl} (HTTP ${r.status})` });
186
+ const hv = r.data || {};
187
+ checks.push({
188
+ key: 'server',
189
+ status: 'ok',
190
+ detail: `${config.serverUrl} (HTTP ${r.status}` +
191
+ (hv.version ? `, server v${hv.version}` : '') +
192
+ (hv.buildTime ? `, built ${hv.buildTime}` : '') +
193
+ ')',
194
+ });
166
195
  }
167
196
  catch (e) {
168
197
  checks.push({ key: 'server', status: 'warn', detail: `${config.serverUrl} unreachable (${e.code || e.message})` });
@@ -227,7 +256,28 @@ export async function runDoctor(opts) {
227
256
  if (serverUp) {
228
257
  await runExtensionCheck(5000);
229
258
  extConnected = true;
230
- checks.push({ key: 'extension_ws', status: 'ok', detail: '扩展已连接, WebSocket 通道就绪' });
259
+ // 证据化输出:列出已连接扩展实例(id/版本/连接时间)。
260
+ // 现场教训:doctor 报"已连"与任务执行时"未连"曾互相矛盾,
261
+ // 把时间戳摆出来用户才能判断报告是否过期(12:03 连过 ≠ 16:32 还连着)。
262
+ let detail = '扩展已连接, WebSocket 通道就绪';
263
+ try {
264
+ const r = await axios.get(`${config.serverUrl}/extensions`, { timeout: 3000, proxy: false });
265
+ const clients = (Array.isArray(r.data) ? r.data : []);
266
+ if (clients.length) {
267
+ const brief = clients
268
+ .map((c) => {
269
+ const at = c.connectedAt ? new Date(c.connectedAt).toLocaleString() : '?';
270
+ return `${c.extensionId ?? 'unknown'}@${c.version ?? '?'} (connectedAt ${at})`;
271
+ })
272
+ .join(', ');
273
+ detail = `扩展已连接 (${clients.length} 实例): ${brief}`;
274
+ }
275
+ else {
276
+ detail = '扩展已连接,但 /extensions 注册表为空(竞态,建议重跑 doctor 确认)';
277
+ }
278
+ }
279
+ catch { /* 证据获取失败不阻断结论 */ }
280
+ checks.push({ key: 'extension_ws', status: 'ok', detail });
231
281
  }
232
282
  else {
233
283
  checks.push({ key: 'extension_ws', status: 'skip', detail: 'server 未运行, 跳过扩展连通检查' });
@@ -256,19 +306,75 @@ export async function runDoctor(opts) {
256
306
  catch { }
257
307
  }
258
308
  }
259
- // ── 5) 登录态(不强制) ──
309
+ // ── 5) 登录态(token 存在性 + 真实探测, fix #8) ──
310
+ // doctor/* 路由本身豁免认证,查 token 存在性会假阳性;
311
+ // 带 token 调受保护端点(/job-cases)才能确认登录真的有效。
260
312
  let loggedIn = false;
261
313
  try {
262
- const { getToken } = await import('../keychain.js');
263
314
  const token = await getToken();
264
- loggedIn = !!token;
265
- checks.push({
266
- key: 'auth',
267
- status: token ? 'ok' : 'warn',
268
- detail: token ? `已登录 (token head=${String(token).slice(0, 6)}...)` : '未登录, 数据面 API 可能受限',
269
- });
270
- if (!token)
315
+ if (!token) {
316
+ checks.push({
317
+ key: 'auth',
318
+ status: 'warn',
319
+ detail: '未登录, 数据面 API 可能受限',
320
+ });
271
321
  hints.push('hunter-mate login');
322
+ }
323
+ else if (!serverUp) {
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
+ }
342
+ }
343
+ else {
344
+ try {
345
+ const r = await axios.get(`${config.serverUrl}/job-cases`, {
346
+ timeout: 5000,
347
+ proxy: false,
348
+ headers: { Authorization: `UTOKEN ${token}` },
349
+ });
350
+ loggedIn = true;
351
+ checks.push({ key: 'auth', status: 'ok', detail: `已登录且 token 有效 (HTTP ${r.status})` });
352
+ }
353
+ catch (e) {
354
+ const status = e?.response?.status;
355
+ if (status === 401 || status === 403) {
356
+ // 真实 fail: server 明确拒绝了这个 token
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
+ }
371
+ }
372
+ else {
373
+ // 网络/其他原因: 标 warn,不误报 fail
374
+ checks.push({ key: 'auth', status: 'warn', detail: `token 存在但探测失败(网络或服务异常): ${e.message}` });
375
+ }
376
+ }
377
+ }
272
378
  }
273
379
  catch (e) {
274
380
  checks.push({ key: 'auth', status: 'skip', detail: `跳过(keychain 读取失败): ${e.message}` });
@@ -6,7 +6,7 @@ import * as path from 'path';
6
6
  import { loadConfig } from '../config.js';
7
7
  import { i18n } from '../i18n/index.js';
8
8
  import { showInstallGuide, runInstall } from './install.js';
9
- import { getCliLogger } from '../logger.js';
9
+ import { printCommandError } from '../utils/command-error.js';
10
10
  const B = {
11
11
  reset: '\x1b[0m', bold: '\x1b[1m',
12
12
  green: '\x1b[32m', yellow: '\x1b[33m', red: '\x1b[31m', cyan: '\x1b[36m', magenta: '\x1b[35m',
@@ -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
- async function httpAuth(baseUrl) {
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,9 +39,8 @@ async function httpAuth(baseUrl) {
37
39
  function requireServerUrl() {
38
40
  const config = loadConfig();
39
41
  if (!config.serverUrl) {
40
- getCliLogger().error('extension:no-server-url', {
41
- hint: '请先 hunter-mate config 设置 serverUrl',
42
- });
42
+ // 同步上下文:手工构造的 Error 无 response.status,不会触发 issuer 诊断分支
43
+ void printCommandError('extension:no-server-url', new Error('请先 hunter-mate config 设置 serverUrl'));
43
44
  process.exit(1);
44
45
  }
45
46
  return config.serverUrl;
@@ -128,9 +129,7 @@ export function ensureEnvReady() {
128
129
  catch { /* ignore */ }
129
130
  }
130
131
  if (!serverUp) {
131
- getCliLogger().error('extension:server-start-failed', {
132
- hint: '请手动执行 hunter-mate start -f',
133
- });
132
+ await printCommandError('extension:server-start-failed', new Error('请手动执行 hunter-mate start -f'));
134
133
  process.exit(1);
135
134
  }
136
135
  // 2) 检查扩展是否安装
@@ -145,9 +144,7 @@ export function ensureEnvReady() {
145
144
  const r = runInstall(init.extensionDir);
146
145
  if (!r.source) {
147
146
  // 内置 dist 不存在, 无法自动安装
148
- getCliLogger().error('extension:auto-install-failed', {
149
- hint: '请在 hunter-mate monorepo 执行 pnpm --filter @hunter-mate/extension build,或手动 hunter-mate install',
150
- });
147
+ await printCommandError('extension:auto-install-failed', new Error('内置 extension dist 不存在,请在 hunter-mate monorepo 执行 pnpm --filter @hunter-mate/extension build,或手动 hunter-mate install'));
151
148
  process.exit(1);
152
149
  }
153
150
  console.log(`${B.green}✓ 扩展已自动安装到 ${r.target}${B.reset}`);
@@ -189,9 +186,7 @@ export function ensureEnvReady() {
189
186
  }
190
187
  catch {
191
188
  // /extensions 端点不可达
192
- getCliLogger().error('extension:check-failed', {
193
- hint: 'Server 可能未正确启动, 请执行 hunter-mate start -f',
194
- });
189
+ await printCommandError('extension:check-failed', new Error('Server 可能未正确启动, 请执行 hunter-mate start -f'));
195
190
  process.exit(1);
196
191
  }
197
192
  })();
@@ -230,7 +225,6 @@ export async function runExtensionCheck(timeoutMs = 5000) {
230
225
  }
231
226
  registered = true;
232
227
  ws.close();
233
- globalThis.__extensionCount = count;
234
228
  resolve();
235
229
  })
236
230
  .catch((err) => {
@@ -256,8 +250,8 @@ async function withEnvCheck(fn) {
256
250
  await fn();
257
251
  }
258
252
  catch (err) {
253
+ await printCommandError('extension:exec-failed', err);
259
254
  const msg = err.response?.data?.message ?? err.response?.data?.error ?? err.message;
260
- getCliLogger().error('extension:exec-failed', { error: msg });
261
255
  if (msg.includes('no Hunter Mate Chrome extension registered') || msg.includes('no Hunter Mate')) {
262
256
  const init = await import('./init.js').then((m) => m.runInit());
263
257
  showInstallGuide(init.extensionDir);
@@ -284,13 +278,11 @@ async function cmdControl(command, argsJson, opts) {
284
278
  params = JSON.parse(argsJson);
285
279
  }
286
280
  catch {
287
- getCliLogger().error('extension:invalid-control-args', {
288
- hint: 'args 必须是合法 JSON 字符串',
289
- });
281
+ console.error('✗ extension:invalid-control-args: args 必须是合法 JSON 字符串');
290
282
  process.exit(1);
291
283
  }
292
284
  }
293
- 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', {
294
286
  command,
295
287
  params,
296
288
  timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
@@ -303,7 +295,7 @@ async function cmdControl(command, argsJson, opts) {
303
295
  async function cmdGoto(url, opts) {
304
296
  return withEnvCheck(async () => {
305
297
  const serverUrl = requireServerUrl();
306
- 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', {
307
299
  url,
308
300
  waitUntil: opts.waitUntil,
309
301
  timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
@@ -316,10 +308,11 @@ async function cmdGoto(url, opts) {
316
308
  async function cmdEval(expression, opts) {
317
309
  return withEnvCheck(async () => {
318
310
  const serverUrl = requireServerUrl();
319
- 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', {
320
312
  expression,
321
313
  timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
322
314
  extensionId: opts.extensionId,
315
+ sessionId: opts.sessionId ?? opts.taskId,
323
316
  });
324
317
  printResult(r.data, opts.format);
325
318
  });
@@ -327,7 +320,7 @@ async function cmdEval(expression, opts) {
327
320
  async function cmdScreenshot(opts) {
328
321
  return withEnvCheck(async () => {
329
322
  const serverUrl = requireServerUrl();
330
- 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', {
331
324
  fullPage: opts.full ? true : undefined,
332
325
  selector: opts.selector,
333
326
  format: opts.imageFormat ?? 'png',
@@ -355,9 +348,7 @@ async function cmdRun(site, command, opts) {
355
348
  params = JSON.parse(opts.args);
356
349
  }
357
350
  catch {
358
- getCliLogger().error('extension:invalid-run-args', {
359
- hint: 'args 必须是合法 JSON 字符串',
360
- });
351
+ console.error('✗ extension:invalid-run-args: args 必须是合法 JSON 字符串');
361
352
  process.exit(1);
362
353
  }
363
354
  }
@@ -366,7 +357,7 @@ async function cmdRun(site, command, opts) {
366
357
  timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
367
358
  extensionId: opts.extensionId,
368
359
  };
369
- 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);
370
361
  printResult(r.data, opts.format);
371
362
  });
372
363
  }
@@ -387,11 +378,14 @@ extensionCommand
387
378
  .action(async () => {
388
379
  try {
389
380
  await ensureEnvReady();
390
- const count = globalThis.__extensionCount ?? 0;
381
+ // fix #31: 用 /extensions 真实查询的计数, 不再读 globalThis.__extensionCount(可能是 stale 0)
382
+ const serverUrl = requireServerUrl();
383
+ const r = await (await httpAuth(serverUrl)).get('/extensions', { timeout: 5000 });
384
+ const count = Array.isArray(r.data) ? r.data.length : 0;
391
385
  console.log(`\n${B.green}✓ Hunter Mate 扩展已连接 (${count} 个浏览器实例)${B.reset}\n`);
392
386
  }
393
387
  catch (err) {
394
- getCliLogger().error('extension:check-error', { error: err.message });
388
+ await printCommandError('extension:check-error', err);
395
389
  process.exit(1);
396
390
  }
397
391
  });
@@ -419,6 +413,6 @@ common(extensionCommand
419
413
  .action(async (opts) => cmdScreenshot(opts));
420
414
  common(extensionCommand
421
415
  .command('run <site> <command>')
422
- .description('站点适配器命令 (对应 chromepilot run): linkedin search|extract|invite 或 resume extract'))
416
+ .description('站点适配器命令 (对应 chromepilot run): linkedin search|extract|invite、liepin search|extract 或 resume extract'))
423
417
  .option('--args <json>', '命令参数 JSON 字符串')
424
418
  .action(async (site, command, opts) => cmdRun(site, command, opts));
@@ -1,8 +1,27 @@
1
1
  import { Command } from 'commander';
2
2
  /**
3
- * 通知所有已连接扩展 reload(自愈主链路):
4
- * 1) 优先 HTTP POST /system/broadcast-extension(返回明确 broadcast 计数和错误)
5
- * 2) Fallback: 直连 WS /ws 发 {type:'broadcast', payload:{type:'reload'}}(兼容旧 server 无 HTTP 端点)
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;
@@ -1,7 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import WebSocket from 'ws';
3
3
  import * as readline from 'readline';
4
- import { loadConfig } from '../config.js';
4
+ import { loadConfig, resolveDataDir } from '../config.js';
5
5
  import axios from 'axios';
6
6
  import * as fs from 'fs';
7
7
  import * as path from 'path';
@@ -10,12 +10,114 @@ import { runStop, tryReadPid } from './server.js';
10
10
  import { i18n } from '../i18n/index.js';
11
11
  import { getCliLogger } from '../logger.js';
12
12
  /**
13
- * 通知所有已连接扩展 reload(自愈主链路):
14
- * 1) 优先 HTTP POST /system/broadcast-extension(返回明确 broadcast 计数和错误)
15
- * 2) Fallback: 直连 WS /ws 发 {type:'broadcast', payload:{type:'reload'}}(兼容旧 server 无 HTTP 端点)
13
+ * FID-092:扫描扩展目录最新 mtime,等待其静止——reload 广播前确认 dist 已写完。
14
+ * 背景:R5 实机 reload 后 3 秒 dist 仍在写入,Chrome 重读读到写了一半的
15
+ * service-worker.js → SW eval 崩溃 → 无 alarm 监听/无 offscreen/零重连,
16
+ * 所有自愈路径全部失效,只能人工点。Windows 兼容:纯 node fs,无 shell。
17
+ */
18
+ export async function waitForExtensionDirQuiet(dir, opts = {}) {
19
+ const quietMs = opts.quietMs ?? 2500;
20
+ const maxWaitMs = opts.maxWaitMs ?? 15000;
21
+ const pollMs = opts.pollMs ?? 400;
22
+ const start = Date.now();
23
+ const newestMtime = (d) => {
24
+ let latest = 0;
25
+ let entries;
26
+ try {
27
+ entries = fs.readdirSync(d, { withFileTypes: true });
28
+ }
29
+ catch {
30
+ return latest; // 目录不可读(未安装/权限)时不阻塞 reload
31
+ }
32
+ for (const e of entries) {
33
+ const full = path.join(d, e.name);
34
+ try {
35
+ if (e.isDirectory()) {
36
+ latest = Math.max(latest, newestMtime(full));
37
+ }
38
+ else {
39
+ latest = Math.max(latest, fs.statSync(full).mtimeMs);
40
+ }
41
+ }
42
+ catch {
43
+ /* 单个文件竞态删除,跳过 */
44
+ }
45
+ }
46
+ return latest;
47
+ };
48
+ let newest = newestMtime(dir);
49
+ while (newest > 0 && Date.now() - newest < quietMs && Date.now() - start < maxWaitMs) {
50
+ await new Promise((r) => setTimeout(r, pollMs));
51
+ newest = newestMtime(dir);
52
+ }
53
+ return { quiet: newest === 0 || Date.now() - newest >= quietMs, waitedMs: Date.now() - start, newestMtimeMs: newest };
54
+ }
55
+ /** FID-092:轮询 server 已连接扩展数(>=1 即判定 reload 后已回连)。 */
56
+ export async function extensionConnectedCount(serverUrl, timeoutMs = 3000) {
57
+ try {
58
+ const r = await axios.get(`${serverUrl}/extensions`, { timeout: timeoutMs, proxy: false });
59
+ return Array.isArray(r.data) ? r.data.length : 0;
60
+ }
61
+ catch {
62
+ return -1; // server 不可达
63
+ }
64
+ }
65
+ /**
66
+ * 通知所有已连接扩展 reload(自愈主链路),FID-092 三段式:
67
+ * 1) dist 静止确认(防 reload 读到写一半的扩展文件)
68
+ * 2) 广播(broadcastReload):优先 HTTP POST /system/broadcast-extension;
69
+ * 旧 server 无该端点时退回直连 WS /ws 广播
70
+ * 3) 健康校验 + 自动重试(广播后 12s 内未回连 → 再广播,最多 3 次)
16
71
  */
17
72
  export async function runReload(timeoutMs = 5000) {
18
73
  const config = loadConfig();
74
+ // FID-092 步骤 1:dist 静止确认(软链/目录均可,扫描真实路径)
75
+ let extDir;
76
+ try {
77
+ extDir = fs.realpathSync(path.join(resolveDataDir(), 'extension'));
78
+ }
79
+ catch {
80
+ extDir = path.join(resolveDataDir(), 'extension');
81
+ }
82
+ const q = await waitForExtensionDirQuiet(extDir);
83
+ const quietNote = q.waitedMs > 500 ? `(等待 dist 写入静止 ${q.waitedMs}ms) ` : '';
84
+ // FID-092 步骤 2+3:广播 → 校验回连 → 失败重试
85
+ const VERIFY_WINDOW_MS = 12000;
86
+ const MAX_ATTEMPTS = 3;
87
+ let last = await broadcastReload(config, timeoutMs);
88
+ if (last.broadcast <= 0) {
89
+ return { ...last, msg: `${quietNote}${last.msg}` };
90
+ }
91
+ const start = Date.now();
92
+ while (Date.now() - start < VERIFY_WINDOW_MS) {
93
+ await new Promise((r) => setTimeout(r, 1000));
94
+ const n = await extensionConnectedCount(config.serverUrl);
95
+ if (n > 0) {
96
+ return { ...last, msg: `${quietNote}${last.msg};已校验扩展回连(connected=${n})。` };
97
+ }
98
+ if (n === -1)
99
+ break; // server 挂了,重试无意义
100
+ }
101
+ // 未回连:重试广播(第一次读到的可能是写了一半的文件,重读即恢复)
102
+ for (let attempt = 2; attempt <= MAX_ATTEMPTS; attempt++) {
103
+ await new Promise((r) => setTimeout(r, 1000));
104
+ last = await broadcastReload(config, timeoutMs);
105
+ if (last.broadcast <= 0)
106
+ break;
107
+ const deadline = Date.now() + VERIFY_WINDOW_MS;
108
+ while (Date.now() < deadline) {
109
+ await new Promise((r) => setTimeout(r, 1000));
110
+ if ((await extensionConnectedCount(config.serverUrl)) > 0) {
111
+ return { ...last, msg: `${quietNote}第 ${attempt} 次广播后已校验扩展回连。` };
112
+ }
113
+ }
114
+ }
115
+ return {
116
+ ...last,
117
+ msg: `${quietNote}${MAX_ATTEMPTS} 次广播后扩展仍未回连(server /extensions=0)——扩展可能处于错误态,请到 chrome://extensions 查看 HunterMate「错误」并点「重新加载」。`,
118
+ };
119
+ }
120
+ async function broadcastReload(config, timeoutMs = 5000) {
19
121
  // ---------- 1) HTTP 优先(POST /system/broadcast-extension) ----------
20
122
  try {
21
123
  const res = await axios.post(`${config.serverUrl}/system/broadcast-extension`, { payload: { type: 'reload' } }, { timeout: timeoutMs, proxy: false });
@@ -29,6 +131,13 @@ export async function runReload(timeoutMs = 5000) {
29
131
  msg: `reload 广播已通过 HTTP 通道发送给 ${broadcast} 个扩展实例:200ms 后每个实例会自动调 chrome.runtime.reload(),Chrome 将重新读取磁盘上的扩展文件。`,
30
132
  };
31
133
  }
134
+ // FID-086 deferred reload: server 已挂起意图,扩展重连(≤30s)自动下发——断连态不失败、不静默
135
+ if (body.queued) {
136
+ return {
137
+ broadcast: 0,
138
+ msg: '扩展当前未连接,重载请求已挂起:扩展重连(通常 ≤30 秒,chrome.alarms 唤醒)后将自动重新加载。无需手动干预。',
139
+ };
140
+ }
32
141
  if (errors.length) {
33
142
  return {
34
143
  broadcast: 0,
@@ -128,7 +237,7 @@ export const reloadCommand = new Command('reload')
128
237
  console.log(r.msg);
129
238
  }
130
239
  catch (e) {
131
- getCliLogger().error('extras:reload-failed', { error: e.message });
240
+ console.error(`✗ extras:reload-failed: ${e.message}`);
132
241
  process.exit(1);
133
242
  }
134
243
  });
@@ -143,7 +252,7 @@ export const openExtCommand = new Command('open-extension-folder')
143
252
  // ---------------------------------------------------------------------------
144
253
  // purge — 彻底清理本地 hunter-mate 数据
145
254
  // ---------------------------------------------------------------------------
146
- const HM_ROOT = () => path.join(process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.', '.hunter-mate');
255
+ const HM_ROOT = resolveDataDir;
147
256
  /** 交互式二次确认 */
148
257
  function confirmPrompt(question) {
149
258
  return new Promise((resolve) => {
@@ -241,7 +350,7 @@ export async function runPurge(opts) {
241
350
  // 3) 停止 server(先于目录删除,避免 server 还在写日志导致删不干净)
242
351
  let serverStopResult = null;
243
352
  if (livePid || exists && fs.existsSync(path.join(root, 'server.pid'))) {
244
- serverStopResult = runStop();
353
+ serverStopResult = await runStop();
245
354
  }
246
355
  // 4) 删除目录
247
356
  let removedFiles = info.files;
@@ -1,5 +1,5 @@
1
1
  import { Command } from 'commander';
2
- /** 对应 ChromePilot init: 创建 ~/.hunter-mate 目录、SQLite、初始 token */
2
+ /** 对应 ChromePilot init: 创建数据目录、SQLite、初始 token(目录位置随 dataDir 配置,FID-071) */
3
3
  export declare function runInit(force?: boolean): {
4
4
  dir: string;
5
5
  extensionDir: string;
@@ -1,12 +1,11 @@
1
1
  import { Command } from 'commander';
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
- import { ensureConfigDir, loadConfig, saveConfig } from '../config.js';
4
+ import { ensureConfigDir, loadConfig, saveConfig, resolveDataDir } from '../config.js';
5
5
  import { getCliLogger } from '../logger.js';
6
- /** 对应 ChromePilot init: 创建 ~/.hunter-mate 目录、SQLite、初始 token */
6
+ /** 对应 ChromePilot init: 创建数据目录、SQLite、初始 token(目录位置随 dataDir 配置,FID-071) */
7
7
  export function runInit(force = false) {
8
- const HOME = process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
9
- const ROOT = path.join(HOME, '.hunter-mate');
8
+ const ROOT = resolveDataDir();
10
9
  const EXT_DIR = path.join(ROOT, 'extension');
11
10
  const DB_FILE = path.join(ROOT, 'hunter-mate.db');
12
11
  const SESSIONS_DIR = path.join(ROOT, 'sessions');
@@ -0,0 +1,23 @@
1
+ import { Command } from 'commander';
2
+ export interface IssueEnvGitInfo {
3
+ repoRoot: string;
4
+ head: string | null;
5
+ branch: string | null;
6
+ /** 最近一次提交(短 hash + 时间 + subject),与 issue 模板「代码 HEAD」行对应 */
7
+ lastCommit: string | null;
8
+ /** 有未提交改动 = dist 可能滞后于 HEAD,提 issue 必须注明 */
9
+ dirty: boolean;
10
+ }
11
+ export interface IssueEnvResult {
12
+ cliVersion: string;
13
+ /** dev = CLI 位于 git 工作区(软链/shim 到源码仓);package = 发布包安装 */
14
+ installType: 'dev' | 'package';
15
+ git: IssueEnvGitInfo | null;
16
+ /** server 未运行时为 null */
17
+ serverBuildTime: string | null;
18
+ os: string;
19
+ node: string;
20
+ dataDir: string;
21
+ }
22
+ export declare function runIssueEnv(): Promise<IssueEnvResult>;
23
+ export declare const issueEnvCommand: Command;