hunter-mate 0.1.7 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/extension/background/service-worker.js +808 -153
- package/dist/assets/extension/build-info.json +1 -0
- package/dist/assets/extension/content/index.js +23 -5
- package/dist/assets/extension/manifest.json +5 -4
- package/dist/assets/extension/offscreen/offscreen.html +8 -0
- package/dist/assets/extension/offscreen/offscreen.js +149 -0
- package/dist/assets/extension/package.json +8 -1
- package/dist/assets/extension/sidepanel/index.css +2 -0
- package/dist/assets/extension/sidepanel/index.html +7 -394
- package/dist/assets/extension/sidepanel/index.js +53 -923
- package/dist/assets/server/app.module.js +11 -1
- package/dist/assets/server/app.module.js.map +1 -1
- package/dist/assets/server/auth/auth.middleware.js +9 -1
- package/dist/assets/server/auth/auth.middleware.js.map +1 -1
- package/dist/assets/server/build-info.json +1 -1
- package/dist/assets/server/customer/customer.controller.d.ts +18 -0
- package/dist/assets/server/customer/customer.controller.js +81 -0
- package/dist/assets/server/customer/customer.controller.js.map +1 -0
- package/dist/assets/server/customer/customer.entity.d.ts +17 -0
- package/dist/assets/server/customer/customer.entity.js +82 -0
- package/dist/assets/server/customer/customer.entity.js.map +1 -0
- package/dist/assets/server/customer/customer.module.d.ts +2 -0
- package/dist/assets/server/customer/customer.module.js +28 -0
- package/dist/assets/server/customer/customer.module.js.map +1 -0
- package/dist/assets/server/customer/customer.service.d.ts +36 -0
- package/dist/assets/server/customer/customer.service.js +200 -0
- package/dist/assets/server/customer/customer.service.js.map +1 -0
- package/dist/assets/server/db/schema-validator.service.d.ts +2 -0
- package/dist/assets/server/db/schema-validator.service.js +32 -1
- package/dist/assets/server/db/schema-validator.service.js.map +1 -1
- package/dist/assets/server/db/sqlite.module.d.ts +1 -0
- package/dist/assets/server/db/sqlite.module.js +12 -2
- package/dist/assets/server/db/sqlite.module.js.map +1 -1
- package/dist/assets/server/devops/devops-sink-init.d.ts +1 -14
- package/dist/assets/server/devops/devops-sink-init.js +3 -8
- package/dist/assets/server/devops/devops-sink-init.js.map +1 -1
- package/dist/assets/server/extension/extension-control.controller.d.ts +17 -1
- package/dist/assets/server/extension/extension-control.controller.js +68 -12
- package/dist/assets/server/extension/extension-control.controller.js.map +1 -1
- package/dist/assets/server/extension/extension-control.service.d.ts +6 -1
- package/dist/assets/server/extension/extension-control.service.js +27 -1
- package/dist/assets/server/extension/extension-control.service.js.map +1 -1
- package/dist/assets/server/extension/extension.module.js +2 -1
- package/dist/assets/server/extension/extension.module.js.map +1 -1
- package/dist/assets/server/extraction/adapter.service.d.ts +3 -0
- package/dist/assets/server/extraction/adapter.service.js +127 -6
- package/dist/assets/server/extraction/adapter.service.js.map +1 -1
- package/dist/assets/server/extraction/anomaly-report.service.js +1 -1
- package/dist/assets/server/extraction/anomaly-report.service.js.map +1 -1
- package/dist/assets/server/extraction/entities/adapter-rule.entity.d.ts +2 -0
- package/dist/assets/server/extraction/entities/adapter-rule.entity.js +10 -0
- package/dist/assets/server/extraction/entities/adapter-rule.entity.js.map +1 -1
- package/dist/assets/server/extraction/extraction-entity.controller.d.ts +21 -3
- package/dist/assets/server/extraction/extraction-entity.controller.js +44 -9
- package/dist/assets/server/extraction/extraction-entity.controller.js.map +1 -1
- package/dist/assets/server/extraction/extraction.module.js +13 -3
- package/dist/assets/server/extraction/extraction.module.js.map +1 -1
- package/dist/assets/server/extraction/extraction.service.d.ts +4 -1
- package/dist/assets/server/extraction/extraction.service.js +71 -13
- package/dist/assets/server/extraction/extraction.service.js.map +1 -1
- package/dist/assets/server/extraction/harness-overlay.service.d.ts +41 -0
- package/dist/assets/server/extraction/harness-overlay.service.js +319 -0
- package/dist/assets/server/extraction/harness-overlay.service.js.map +1 -0
- package/dist/assets/server/extraction/liepin-extractor.d.ts +1 -0
- package/dist/assets/server/extraction/liepin-extractor.js +172 -0
- package/dist/assets/server/extraction/liepin-extractor.js.map +1 -0
- package/dist/assets/server/extraction/script-rule.js +14 -2
- package/dist/assets/server/extraction/script-rule.js.map +1 -1
- package/dist/assets/server/governance/action-audit.entity.d.ts +12 -0
- package/dist/assets/server/governance/action-audit.entity.js +66 -0
- package/dist/assets/server/governance/action-audit.entity.js.map +1 -0
- package/dist/assets/server/governance/governance.command-recorder.d.ts +17 -0
- package/dist/assets/server/governance/governance.command-recorder.js +98 -0
- package/dist/assets/server/governance/governance.command-recorder.js.map +1 -0
- package/dist/assets/server/governance/governance.controller.d.ts +14 -0
- package/dist/assets/server/governance/governance.controller.js +77 -0
- package/dist/assets/server/governance/governance.controller.js.map +1 -0
- package/dist/assets/server/governance/governance.ingester.d.ts +21 -0
- package/dist/assets/server/governance/governance.ingester.js +117 -0
- package/dist/assets/server/governance/governance.ingester.js.map +1 -0
- package/dist/assets/server/governance/governance.module.d.ts +2 -0
- package/dist/assets/server/governance/governance.module.js +27 -0
- package/dist/assets/server/governance/governance.module.js.map +1 -0
- package/dist/assets/server/governance/governance.service.d.ts +122 -0
- package/dist/assets/server/governance/governance.service.js +398 -0
- package/dist/assets/server/governance/governance.service.js.map +1 -0
- package/dist/assets/server/jobcase/jobcase.controller.d.ts +1 -0
- package/dist/assets/server/jobcase/jobcase.entity.d.ts +1 -0
- package/dist/assets/server/jobcase/jobcase.entity.js +6 -0
- package/dist/assets/server/jobcase/jobcase.entity.js.map +1 -1
- package/dist/assets/server/jobcase/jobcase.service.d.ts +2 -0
- package/dist/assets/server/jobcase/jobcase.service.js +2 -1
- package/dist/assets/server/jobcase/jobcase.service.js.map +1 -1
- package/dist/assets/server/main.js +1 -0
- package/dist/assets/server/main.js.map +1 -1
- package/dist/assets/server/package.json +1 -1
- package/dist/assets/server/preference/preference.controller.d.ts +14 -0
- package/dist/assets/server/preference/preference.controller.js +66 -0
- package/dist/assets/server/preference/preference.controller.js.map +1 -0
- package/dist/assets/server/preference/preference.entity.d.ts +13 -0
- package/dist/assets/server/preference/preference.entity.js +77 -0
- package/dist/assets/server/preference/preference.entity.js.map +1 -0
- package/dist/assets/server/preference/preference.module.d.ts +2 -0
- package/dist/assets/server/preference/preference.module.js +26 -0
- package/dist/assets/server/preference/preference.module.js.map +1 -0
- package/dist/assets/server/preference/preference.service.d.ts +33 -0
- package/dist/assets/server/preference/preference.service.js +122 -0
- package/dist/assets/server/preference/preference.service.js.map +1 -0
- package/dist/assets/server/recording/recording-event-queue.service.js +1 -1
- package/dist/assets/server/recording/recording-event-queue.service.js.map +1 -1
- package/dist/assets/server/recording/recording-retention.service.d.ts +19 -2
- package/dist/assets/server/recording/recording-retention.service.js +95 -6
- package/dist/assets/server/recording/recording-retention.service.js.map +1 -1
- package/dist/assets/server/recording/recording-snapshot.service.d.ts +1 -0
- package/dist/assets/server/recording/recording-snapshot.service.js +4 -0
- package/dist/assets/server/recording/recording-snapshot.service.js.map +1 -1
- package/dist/assets/server/recording/recording.controller.d.ts +10 -0
- package/dist/assets/server/recording/recording.controller.js +19 -0
- package/dist/assets/server/recording/recording.controller.js.map +1 -1
- package/dist/assets/server/recording/recording.gateway.js +15 -0
- package/dist/assets/server/recording/recording.gateway.js.map +1 -1
- package/dist/assets/server/recording/recording.module.js +6 -1
- package/dist/assets/server/recording/recording.module.js.map +1 -1
- package/dist/assets/server/recording/recording.service.d.ts +5 -0
- package/dist/assets/server/recording/recording.service.js +10 -0
- package/dist/assets/server/recording/recording.service.js.map +1 -1
- package/dist/assets/server/remark/remark.controller.d.ts +1 -1
- package/dist/assets/server/remark/remark.controller.js +1 -10
- package/dist/assets/server/remark/remark.controller.js.map +1 -1
- package/dist/assets/server/remark/remark.entity.d.ts +1 -1
- package/dist/assets/server/remark/remark.entity.js.map +1 -1
- package/dist/assets/server/site/entities/site-registry.entity.d.ts +1 -1
- package/dist/assets/server/site/entities/site-registry.entity.js +1 -1
- package/dist/assets/server/site/entities/site-registry.entity.js.map +1 -1
- package/dist/assets/server/site/site.controller.d.ts +1 -1
- package/dist/assets/server/site/site.service.d.ts +5 -1
- package/dist/assets/server/site/site.service.js +13 -0
- package/dist/assets/server/site/site.service.js.map +1 -1
- package/dist/assets/server/system/system.controller.d.ts +1 -0
- package/dist/assets/server/system/system.controller.js +1 -1
- package/dist/assets/server/system/system.controller.js.map +1 -1
- package/dist/assets/server/system/system.service.d.ts +1 -0
- package/dist/assets/server/system/system.service.js +3 -2
- package/dist/assets/server/system/system.service.js.map +1 -1
- package/dist/cache/db.js +6 -6
- package/dist/commands/addons.js +2 -1
- package/dist/commands/cache.js +1 -1
- package/dist/commands/case.d.ts +67 -0
- package/dist/commands/case.js +101 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.js +8 -1
- package/dist/commands/customer.d.ts +25 -0
- package/dist/commands/customer.js +137 -0
- package/dist/commands/doctor.js +33 -9
- package/dist/commands/extension.js +21 -16
- package/dist/commands/extras.d.ts +18 -3
- package/dist/commands/extras.js +162 -5
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +3 -4
- package/dist/commands/install.d.ts +5 -0
- package/dist/commands/install.js +22 -42
- package/dist/commands/issue-env.d.ts +23 -0
- package/dist/commands/issue-env.js +101 -0
- package/dist/commands/job.d.ts +2 -1
- package/dist/commands/job.js +25 -5
- package/dist/commands/login.d.ts +4 -0
- package/dist/commands/login.js +57 -4
- package/dist/commands/migrate.d.ts +32 -0
- package/dist/commands/migrate.js +116 -0
- package/dist/commands/note.d.ts +3 -0
- package/dist/commands/note.js +75 -0
- package/dist/commands/ops.js +64 -13
- package/dist/commands/preference.d.ts +6 -0
- package/dist/commands/preference.js +80 -0
- package/dist/commands/record.js +1 -1
- package/dist/commands/report.js +1 -1
- package/dist/commands/search.js +1 -1
- package/dist/commands/server.d.ts +12 -0
- package/dist/commands/server.js +182 -18
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.js +16 -6
- package/dist/commands/update.js +7 -8
- package/dist/config.d.ts +21 -0
- package/dist/config.js +27 -0
- package/dist/extension-sync.d.ts +35 -0
- package/dist/extension-sync.js +159 -0
- package/dist/i18n/locales/en-US.json +17 -1
- package/dist/i18n/locales/zh-CN.json +17 -1
- package/dist/index.js +17 -0
- package/dist/keychain.d.ts +16 -1
- package/dist/keychain.js +43 -16
- package/dist/logger.js +2 -2
- package/dist/postinstall.d.ts +1 -0
- package/dist/postinstall.js +31 -0
- package/dist/updater.js +2 -2
- package/dist/utils/artifact-checksum.d.ts +4 -0
- package/dist/utils/artifact-checksum.js +40 -0
- package/dist/utils/command-error.d.ts +4 -2
- package/dist/utils/command-error.js +19 -3
- package/dist/utils/link.d.ts +18 -0
- package/dist/utils/link.js +83 -0
- package/dist/utils/local-http.d.ts +7 -0
- package/dist/utils/local-http.js +30 -0
- package/dist/version.js +86 -49
- package/dist/watchdog.d.ts +8 -0
- package/dist/watchdog.js +49 -9
- package/dist/web/dist/404.html +1 -1
- package/dist/web/dist/_next/static/chunks/app/logs/page-d478031d49f7ba00.js +1 -0
- package/dist/web/dist/_next/static/chunks/{main-app-80b038a312917d90.js → main-app-0af848156a5b13fc.js} +1 -1
- package/dist/web/dist/_next/static/chunks/pages/{_app-da7d628f49271ece.js → _app-3d844792ec91e4d1.js} +2 -2
- package/dist/web/dist/auth/verify.html +1 -1
- package/dist/web/dist/auth/verify.txt +1 -1
- package/dist/web/dist/companies.html +1 -1
- package/dist/web/dist/companies.txt +1 -1
- package/dist/web/dist/entity.html +1 -1
- package/dist/web/dist/entity.txt +1 -1
- package/dist/web/dist/index.html +1 -1
- package/dist/web/dist/index.txt +1 -1
- package/dist/web/dist/jobs.html +1 -1
- package/dist/web/dist/jobs.txt +1 -1
- package/dist/web/dist/logs.html +1 -1
- package/dist/web/dist/logs.txt +2 -2
- package/dist/web/dist/matching/jobs/placeholder/talents.html +1 -1
- package/dist/web/dist/matching/jobs/placeholder/talents.txt +1 -1
- package/dist/web/dist/matching/portraits/placeholder/jobs.html +1 -1
- package/dist/web/dist/matching/portraits/placeholder/jobs.txt +1 -1
- package/dist/web/dist/parselog.html +1 -1
- package/dist/web/dist/parselog.txt +1 -1
- package/dist/web/dist/recordings.html +1 -1
- package/dist/web/dist/recordings.txt +1 -1
- package/dist/web/dist/snapshots/placeholder/agent-prompt.html +1 -1
- package/dist/web/dist/snapshots/placeholder/agent-prompt.txt +1 -1
- package/dist/web/dist/talents.html +1 -1
- package/dist/web/dist/talents.txt +1 -1
- package/package.json +6 -4
- package/scripts/postinstall-run.mjs +14 -0
- package/dist/web/dist/_next/static/chunks/app/logs/page-9f41d44fa1add723.js +0 -1
- /package/dist/web/dist/_next/static/{hqJ-7vr4X71DNfzZugHJ- → 3bVkK4pAgbbmp5r0ThyBL}/_buildManifest.js +0 -0
- /package/dist/web/dist/_next/static/{hqJ-7vr4X71DNfzZugHJ- → 3bVkK4pAgbbmp5r0ThyBL}/_ssgManifest.js +0 -0
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
type StartLock = 'acquired' | 'busy' | 'stolen';
|
|
3
|
+
/** FID-093:导出供单测覆盖竞态/残留锁语义 */
|
|
4
|
+
export declare function acquireStartLock(): StartLock;
|
|
5
|
+
/**
|
|
6
|
+
* server.log 无界增长曾达 800M+ 挤占用户磁盘(FID-068)。
|
|
7
|
+
* 后台 daemon 启动前按大小轮转:超过 HM_LOG_MAX_BYTES(默认 50MB)时
|
|
8
|
+
* server.log → server.log.1 → …保留最近 LOG_KEEP_FILES 份,最旧丢弃。
|
|
9
|
+
* 纯 fs 实现,Windows 兼容(rename 前先清目标,Windows 不允许覆盖已有文件)。
|
|
10
|
+
*/
|
|
11
|
+
export declare function rotateLogIfNeeded(logFile: string, maxBytes?: number, keep?: number): boolean;
|
|
2
12
|
declare function tryReadPid(): number | null;
|
|
3
13
|
export { tryReadPid };
|
|
4
14
|
/** 定位 server 包根(dist 优先,ts-node 开发模式兜底);找不到返回 null */
|
|
5
15
|
export declare function findServerRoot(): string | null;
|
|
16
|
+
/** 探活:server /health 是否可访问(短超时,永不 throw)。导出供 version.ts 重启编排复核 */
|
|
17
|
+
export declare function probeServerHealth(url: string, timeoutMs?: number): Promise<boolean>;
|
|
6
18
|
/** 对应 ChromePilot start: 启动本地 NestJS Server(提供 REST + WebSocket + SPA) */
|
|
7
19
|
export declare function runStart(opts: {
|
|
8
20
|
port?: number;
|
package/dist/commands/server.js
CHANGED
|
@@ -3,13 +3,69 @@ 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');
|
|
14
|
+
/**
|
|
15
|
+
* FID-093:启动单飞锁。N 个 CLI 进程并发 auto-start(如多个 agent 同时跑
|
|
16
|
+
* extension 命令)时,旧逻辑各自 spawn watchdog——即便 watchdog 单例加固后
|
|
17
|
+
* 多实例会被拒,也会留下一地被误写的 pid 文件与误导性的"启动失败"输出。
|
|
18
|
+
* 持锁者完成 spawn + health 探测期间,其他进程改为等待并复用结果。
|
|
19
|
+
* 锁残留(持锁进程崩溃)按 mtime 判 stale 抢占。
|
|
20
|
+
*/
|
|
21
|
+
const START_LOCK_FILE = () => path.join(resolveDataDir(), 'server-start.lock');
|
|
22
|
+
const START_LOCK_STALE_MS = 90_000;
|
|
23
|
+
/** FID-093:导出供单测覆盖竞态/残留锁语义 */
|
|
24
|
+
export function acquireStartLock() {
|
|
25
|
+
const write = () => {
|
|
26
|
+
try {
|
|
27
|
+
const fd = fs.openSync(START_LOCK_FILE(), 'wx'); // 原子 O_CREAT|O_EXCL
|
|
28
|
+
try {
|
|
29
|
+
fs.writeFileSync(fd, String(process.pid));
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
fs.closeSync(fd);
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
if (write())
|
|
41
|
+
return 'acquired';
|
|
42
|
+
try {
|
|
43
|
+
if (Date.now() - fs.statSync(START_LOCK_FILE()).mtimeMs > START_LOCK_STALE_MS) {
|
|
44
|
+
fs.unlinkSync(START_LOCK_FILE());
|
|
45
|
+
if (write())
|
|
46
|
+
return 'stolen';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch { /* 竞态:他人同时清理/占用,让出 */ }
|
|
50
|
+
return 'busy';
|
|
51
|
+
}
|
|
52
|
+
function releaseStartLock() {
|
|
53
|
+
try {
|
|
54
|
+
fs.unlinkSync(START_LOCK_FILE());
|
|
55
|
+
}
|
|
56
|
+
catch { /* ignore */ }
|
|
57
|
+
}
|
|
58
|
+
function watchdogAlive(pid) {
|
|
59
|
+
if (!pid)
|
|
60
|
+
return false;
|
|
61
|
+
try {
|
|
62
|
+
process.kill(pid, 0);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
13
69
|
function tryReadPidFile(file) {
|
|
14
70
|
try {
|
|
15
71
|
const raw = fs.readFileSync(file, 'utf-8').trim();
|
|
@@ -29,6 +85,38 @@ function cleanPidFile(file) {
|
|
|
29
85
|
}
|
|
30
86
|
catch { /* ignore */ }
|
|
31
87
|
}
|
|
88
|
+
const LOG_MAX_BYTES = () => Number(process.env.HM_LOG_MAX_BYTES || 50 * 1024 * 1024);
|
|
89
|
+
const LOG_KEEP_FILES = 3;
|
|
90
|
+
/**
|
|
91
|
+
* server.log 无界增长曾达 800M+ 挤占用户磁盘(FID-068)。
|
|
92
|
+
* 后台 daemon 启动前按大小轮转:超过 HM_LOG_MAX_BYTES(默认 50MB)时
|
|
93
|
+
* server.log → server.log.1 → …保留最近 LOG_KEEP_FILES 份,最旧丢弃。
|
|
94
|
+
* 纯 fs 实现,Windows 兼容(rename 前先清目标,Windows 不允许覆盖已有文件)。
|
|
95
|
+
*/
|
|
96
|
+
export function rotateLogIfNeeded(logFile, maxBytes = LOG_MAX_BYTES(), keep = LOG_KEEP_FILES) {
|
|
97
|
+
try {
|
|
98
|
+
if (!fs.existsSync(logFile))
|
|
99
|
+
return false;
|
|
100
|
+
if (fs.statSync(logFile).size <= maxBytes)
|
|
101
|
+
return false;
|
|
102
|
+
for (let i = keep - 1; i >= 1; i--) {
|
|
103
|
+
const src = `${logFile}.${i}`;
|
|
104
|
+
const dest = `${logFile}.${i + 1}`;
|
|
105
|
+
if (!fs.existsSync(src))
|
|
106
|
+
continue;
|
|
107
|
+
try {
|
|
108
|
+
fs.unlinkSync(dest);
|
|
109
|
+
}
|
|
110
|
+
catch { /* dest 不存在时忽略 */ }
|
|
111
|
+
fs.renameSync(src, dest);
|
|
112
|
+
}
|
|
113
|
+
fs.renameSync(logFile, `${logFile}.1`);
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
32
120
|
function tryReadPid() {
|
|
33
121
|
const pid = tryReadPidFile(PID_FILE());
|
|
34
122
|
if (pid)
|
|
@@ -55,8 +143,8 @@ export function findServerRoot() {
|
|
|
55
143
|
}
|
|
56
144
|
return null;
|
|
57
145
|
}
|
|
58
|
-
/** 探活:server /health 是否可访问(短超时,永不 throw) */
|
|
59
|
-
async function probeServerHealth(url, timeoutMs = 1500) {
|
|
146
|
+
/** 探活:server /health 是否可访问(短超时,永不 throw)。导出供 version.ts 重启编排复核 */
|
|
147
|
+
export async function probeServerHealth(url, timeoutMs = 1500) {
|
|
60
148
|
try {
|
|
61
149
|
const res = await fetch(`${url}/health`, { signal: AbortSignal.timeout(timeoutMs) });
|
|
62
150
|
return res.ok;
|
|
@@ -78,6 +166,42 @@ export async function runStart(opts) {
|
|
|
78
166
|
const url = `http://${host}:${port}`;
|
|
79
167
|
// 启动过程中的告警说明(会拼进最终返回 msg,保证用户看得见)
|
|
80
168
|
const notes = [];
|
|
169
|
+
// FID-093 单飞:另一进程正在启动时等待复用,不再并发起 watchdog
|
|
170
|
+
const lock = acquireStartLock();
|
|
171
|
+
if (lock === 'busy') {
|
|
172
|
+
const deadline = Date.now() + 35_000;
|
|
173
|
+
while (Date.now() < deadline) {
|
|
174
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
175
|
+
if (await probeServerHealth(url)) {
|
|
176
|
+
return {
|
|
177
|
+
started: true,
|
|
178
|
+
pid: null,
|
|
179
|
+
url,
|
|
180
|
+
msg: 'server 已由另一个 hunter-mate 进程启动完成(启动锁被持有,本次复用其结果)',
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
started: false,
|
|
186
|
+
pid: null,
|
|
187
|
+
url,
|
|
188
|
+
msg: '另一个 hunter-mate 进程持有 server 启动锁,且 35s 内 /health 未就绪。请执行 hunter-mate doctor 排查。',
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (lock === 'stolen') {
|
|
192
|
+
notes.push('⚠ 检测到残留的 server-start.lock(持有进程疑似崩溃),已按 stale 抢占继续启动');
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
return await runStartLocked(opts, { url, port, host, notes });
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
releaseStartLock();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/** 单飞锁内的实际启动逻辑(锁外逻辑见 runStart) */
|
|
202
|
+
async function runStartLocked(opts, ctx) {
|
|
203
|
+
const { url, port, host, notes } = ctx;
|
|
204
|
+
const config = loadConfig();
|
|
81
205
|
const existing = tryReadPid();
|
|
82
206
|
if (existing) {
|
|
83
207
|
// pid 文件存在 ≠ server 存活。pid 可能被系统中其他进程复用
|
|
@@ -94,9 +218,42 @@ export async function runStart(opts) {
|
|
|
94
218
|
}
|
|
95
219
|
return { started: false, pid: existing, url, msg: `server already running (pid=${existing})` };
|
|
96
220
|
}
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
|
|
221
|
+
// FID-093:探活失败≠server 死了。机器抖动(构建/多实例抢 CPU)会让单次
|
|
222
|
+
// /health 超时,旧逻辑直接当 stale 清文件重拉 → 活 server 被孤儿化,
|
|
223
|
+
// 新实例 EADDRINUSE 崩溃循环(2026-09-10 23:13 实机复现)。先给 grace 窗口
|
|
224
|
+
// 复探:进程仍存活且 /health 恢复 → 按已运行处理,绝不双实例。
|
|
225
|
+
let recovered = false;
|
|
226
|
+
const graceDeadline = Date.now() + 10_000;
|
|
227
|
+
while (Date.now() < graceDeadline) {
|
|
228
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
229
|
+
const pidAlive = (() => {
|
|
230
|
+
try {
|
|
231
|
+
process.kill(existing, 0);
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
})();
|
|
238
|
+
if (!pidAlive)
|
|
239
|
+
break; // 真死了,走正常启动
|
|
240
|
+
if (await probeServerHealth(url)) {
|
|
241
|
+
recovered = true;
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (recovered) {
|
|
246
|
+
return {
|
|
247
|
+
started: false,
|
|
248
|
+
pid: existing,
|
|
249
|
+
url,
|
|
250
|
+
msg: `server already running (pid=${existing}; /health 曾短暂无响应,grace 窗口内恢复,未拉起新实例)`,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
// stale pid:进程号存活但 grace 后仍无响应(疑似僵死或被其他进程复用)。清理后继续启动,
|
|
254
|
+
// 不 kill —— 那个进程可能属于别的程序(误杀 foreign 进程红线);
|
|
255
|
+
// 若它其实占了 34570 端口,新实例会因 EADDRINUSE 显式崩溃退出,不静默。
|
|
256
|
+
notes.push(`⚠ 检测到 stale server.pid (pid=${existing}):该进程存活但 ${url}/health 在 10s grace 窗口内持续无响应,` +
|
|
100
257
|
`疑似 pid 被其他进程复用,已忽略并继续启动`);
|
|
101
258
|
cleanPidFile(PID_FILE());
|
|
102
259
|
}
|
|
@@ -131,6 +288,8 @@ export async function runStart(opts) {
|
|
|
131
288
|
...process.env,
|
|
132
289
|
PORT: String(port),
|
|
133
290
|
HOST: host,
|
|
291
|
+
// FID-071: server 所有数据落点(db/备份/runtime.json/artifacts)由 CLI 的数据目录决定
|
|
292
|
+
HM_DATA_DIR: resolveDataDir(),
|
|
134
293
|
};
|
|
135
294
|
const nodeArgs = [];
|
|
136
295
|
if (entry.endsWith('.ts')) {
|
|
@@ -148,6 +307,8 @@ export async function runStart(opts) {
|
|
|
148
307
|
const logDir = path.dirname(LOG_FILE());
|
|
149
308
|
if (!fs.existsSync(logDir))
|
|
150
309
|
fs.mkdirSync(logDir, { recursive: true });
|
|
310
|
+
// 后台 daemon 启动前轮转 server.log(超 50MB 保留 3 份),防止无界增长挤占磁盘
|
|
311
|
+
rotateLogIfNeeded(LOG_FILE());
|
|
151
312
|
// 后台模式由独立 watchdog 进程托管 server,实现崩溃后自动重启
|
|
152
313
|
const watchdogEntry = path.resolve(__dirname, '../watchdog.js');
|
|
153
314
|
if (!fs.existsSync(watchdogEntry)) {
|
|
@@ -163,6 +324,7 @@ export async function runStart(opts) {
|
|
|
163
324
|
HM_SERVER_ENTRY: entry,
|
|
164
325
|
HM_SERVER_LOG: LOG_FILE(),
|
|
165
326
|
HM_SERVER_NODE_ARGS: nodeArgs.join(','),
|
|
327
|
+
HM_DATA_DIR: resolveDataDir(),
|
|
166
328
|
PORT: String(port),
|
|
167
329
|
HOST: host,
|
|
168
330
|
};
|
|
@@ -173,8 +335,9 @@ export async function runStart(opts) {
|
|
|
173
335
|
windowsHide: true,
|
|
174
336
|
});
|
|
175
337
|
wd.unref();
|
|
176
|
-
|
|
177
|
-
|
|
338
|
+
// FID-093:不再由 CLI 覆写 watchdog.pid——watchdog 进程原子自注册(acquireWatchdogLock),
|
|
339
|
+
// 并发启动的败者若覆写会把胜者 pid 覆盖成将死进程,runStop 从此找不到活 watchdog。
|
|
340
|
+
// 就绪探测改为直接探 spawn 出的 watchdog 进程是否存活(比 pid 文件更直接)。
|
|
178
341
|
// 等待 server health 就绪。窗口给足:NestJS 全量路由映射 + TypeORM schema 同步
|
|
179
342
|
// 在慢机/Windows 上实测 10-15s,原 8s 会在 route mapping 阶段误报"未就绪"
|
|
180
343
|
// (server 实际 3 秒后就绪,watchdog 会保活,误报只会误导用户)。
|
|
@@ -191,8 +354,8 @@ export async function runStart(opts) {
|
|
|
191
354
|
}
|
|
192
355
|
}
|
|
193
356
|
catch { /* server 尚未监听,继续轮询 */ }
|
|
194
|
-
// watchdog 自身退出说明启动参数/入口有问题
|
|
195
|
-
if (!
|
|
357
|
+
// watchdog 自身退出说明启动参数/入口有问题(含单例锁被占,另一 watchdog 在保活)
|
|
358
|
+
if (!watchdogAlive(wd.pid ?? null))
|
|
196
359
|
break;
|
|
197
360
|
}
|
|
198
361
|
if (!ready) {
|
|
@@ -240,14 +403,15 @@ export async function runStop() {
|
|
|
240
403
|
const wdPid = tryReadPidFile(WATCHDOG_PID_FILE());
|
|
241
404
|
if (wdPid) {
|
|
242
405
|
if (!healthy) {
|
|
243
|
-
// server 不响应时不杀 watchdog pid(可能已被其他进程复用)
|
|
244
|
-
|
|
245
|
-
|
|
406
|
+
// server 不响应时不杀 watchdog pid(可能已被其他进程复用)——红线不动;
|
|
407
|
+
// FID-093:也不清 pid 文件——进程可能还活着,清了等于孤儿化注册表,
|
|
408
|
+
// 后续 auto-start 会再拉一个实例造成双 server(EADDRINUSE 崩溃循环)。
|
|
409
|
+
// 文件留在原地,runStart 的 grace 复探会正确处理。
|
|
246
410
|
return {
|
|
247
411
|
stopped: false,
|
|
248
412
|
pid: wdPid,
|
|
249
413
|
msg: `✗ server 无响应(${url}/health 不可达),watchdog pid=${wdPid} 未杀(疑似 stale pid,` +
|
|
250
|
-
`可能已被其他进程复用)
|
|
414
|
+
`可能已被其他进程复用)。pid 文件保留以便复探;如确认是僵死进程请手工 kill`,
|
|
251
415
|
};
|
|
252
416
|
}
|
|
253
417
|
const ok = killGracefullySync(wdPid);
|
|
@@ -270,13 +434,13 @@ export async function runStop() {
|
|
|
270
434
|
if (!pid)
|
|
271
435
|
return { stopped: false, pid: null, msg: 'no server.pid found or process dead' };
|
|
272
436
|
if (!healthy) {
|
|
273
|
-
// pid 存活但 server 不响应:不 kill(可能复用了别人的 pid)
|
|
274
|
-
|
|
437
|
+
// pid 存活但 server 不响应:不 kill(可能复用了别人的 pid)红线不动;
|
|
438
|
+
// FID-093:同样保留 pid 文件(进程可能还活着,清了会孤儿化注册表)
|
|
275
439
|
return {
|
|
276
440
|
stopped: false,
|
|
277
441
|
pid,
|
|
278
442
|
msg: `✗ pid=${pid} 存活但 server 无响应,未发送 kill 信号(疑似 stale pid,` +
|
|
279
|
-
`可能属于其他进程)
|
|
443
|
+
`可能属于其他进程)。pid 文件保留以便复探;如确认是僵死 server 请手工 kill`,
|
|
280
444
|
};
|
|
281
445
|
}
|
|
282
446
|
const ok = killGracefullySync(pid);
|
|
@@ -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();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** 定位 CLI 内置 extension dist(npm 包 assets 优先,monorepo 并列路径兜底) */
|
|
2
|
+
export declare function resolveBuiltinExtensionDist(): string | null;
|
|
3
|
+
export interface ExtensionArtifactsCompare {
|
|
4
|
+
/** 上游 = CLI 包内置产物目录;npm 包缺资产时为 null */
|
|
5
|
+
source: string | null;
|
|
6
|
+
/** 下游 = ~/.hunter-mate/extension */
|
|
7
|
+
target: string;
|
|
8
|
+
sourceChecksum: string | null;
|
|
9
|
+
targetChecksum: string | null;
|
|
10
|
+
sourceVersion: string | null;
|
|
11
|
+
targetVersion: string | null;
|
|
12
|
+
/** source 存在且 target 存在且 checksum 不同(上游升级了/下游被改过) */
|
|
13
|
+
stale: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function compareExtensionArtifacts(targetDir?: string): ExtensionArtifactsCompare;
|
|
16
|
+
export interface ExtensionSyncResult {
|
|
17
|
+
synced: boolean;
|
|
18
|
+
reason: string;
|
|
19
|
+
copied: number;
|
|
20
|
+
removed: number;
|
|
21
|
+
fromVersion?: string;
|
|
22
|
+
toVersion?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 同步内置扩展产物到目标目录。
|
|
26
|
+
* - source 缺失(monorepo 未构建/npm 包缺资产)→ no-op
|
|
27
|
+
* - target 缺失 → 默认 no-op(首次安装走 hunter-mate install 引导);
|
|
28
|
+
* install 命令传 ensureTarget=true 创建
|
|
29
|
+
* - target 是软链(dev 模式)→ no-op,保护开发链
|
|
30
|
+
* - checksum 一致 → no-op;不一致 → 剪枝(删目标多余文件)+复制
|
|
31
|
+
*/
|
|
32
|
+
export declare function syncExtensionArtifacts(opts?: {
|
|
33
|
+
target?: string;
|
|
34
|
+
ensureTarget?: boolean;
|
|
35
|
+
}): ExtensionSyncResult;
|