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
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// extension-sync.ts — CLI 内置扩展产物 ↔ ~/.hunter-mate/extension 同步(Issue #23)
|
|
2
|
+
//
|
|
3
|
+
// 背景:npm 升级 CLI 后,~/.hunter-mate/extension 仍是旧版构建(update 一致性
|
|
4
|
+
// 检查只看"运行中扩展 vs 磁盘",二者同为旧版所以误判"无需 reload")。本模块以
|
|
5
|
+
// CLI 包内置产物(dist/assets/extension)为上游 SSOT,按目录 checksum 比对,
|
|
6
|
+
// 不一致则剪枝+复制,三处接线:
|
|
7
|
+
// 1) npm postinstall(升级 CLI 即自动同步,postinstall.ts)
|
|
8
|
+
// 2) update --apply / 一致性兜底(ensureRuntimeConsistency,先同步再广播 reload)
|
|
9
|
+
// 3) hunter-mate install(复用同一同步逻辑,替代旧的只增不剪 copy)
|
|
10
|
+
//
|
|
11
|
+
// 约束:永不抛异常(调用方含 postinstall,绝不能让 npm install 失败);
|
|
12
|
+
// 目标目录是软链(dev 开发模式)时跳过,不往里写。
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { findBundledAssets } from './bundled-assets.js';
|
|
17
|
+
import { dirChecksum } from './utils/artifact-checksum.js';
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
/** 定位 CLI 内置 extension dist(npm 包 assets 优先,monorepo 并列路径兜底) */
|
|
20
|
+
export function resolveBuiltinExtensionDist() {
|
|
21
|
+
// 显式覆盖(测试夹具/应急指定),优先于一切自动探测;
|
|
22
|
+
// 设为空字符串 = 强制禁用自动探测(测试 no-bundled-assets 路径)
|
|
23
|
+
const override = process.env.HM_BUNDLED_EXTENSION_DIR;
|
|
24
|
+
if (override !== undefined)
|
|
25
|
+
return override && fs.existsSync(override) ? override : null;
|
|
26
|
+
// 优先 bundled-assets(npm 安装模式:dist/assets/extension 已存在)
|
|
27
|
+
const bundled = findBundledAssets();
|
|
28
|
+
if (bundled)
|
|
29
|
+
return bundled.extensionDir;
|
|
30
|
+
// fallback:monorepo 并列路径(老 hunter-mate 开发模式)
|
|
31
|
+
const candidates = [
|
|
32
|
+
path.resolve(__dirname, '../../extension/dist'), // src 布局: src -> packages/extension/dist
|
|
33
|
+
path.resolve(__dirname, '../../../extension/dist'), // dist 布局: dist/commands -> packages/extension/dist
|
|
34
|
+
path.resolve(__dirname, '../bundled-extension'), // bundled
|
|
35
|
+
];
|
|
36
|
+
for (const c of candidates)
|
|
37
|
+
if (fs.existsSync(c))
|
|
38
|
+
return c;
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/** build-info.json(FID-092 起构建注入)优先,manifest.json version 兜底 */
|
|
42
|
+
function readArtifactVersion(dir) {
|
|
43
|
+
try {
|
|
44
|
+
const info = JSON.parse(fs.readFileSync(path.join(dir, 'build-info.json'), 'utf-8'));
|
|
45
|
+
if (info && typeof info.version === 'string' && info.version)
|
|
46
|
+
return info.version;
|
|
47
|
+
}
|
|
48
|
+
catch { /* 无 build-info */ }
|
|
49
|
+
try {
|
|
50
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(dir, 'manifest.json'), 'utf-8'));
|
|
51
|
+
if (manifest && typeof manifest.version === 'string' && manifest.version)
|
|
52
|
+
return manifest.version;
|
|
53
|
+
}
|
|
54
|
+
catch { /* 无 manifest */ }
|
|
55
|
+
return '0.0.0';
|
|
56
|
+
}
|
|
57
|
+
export function compareExtensionArtifacts(targetDir) {
|
|
58
|
+
const target = targetDir ?? defaultTargetDir();
|
|
59
|
+
const source = resolveBuiltinExtensionDist();
|
|
60
|
+
const sourceChecksum = source ? dirChecksum(source).checksum : null;
|
|
61
|
+
const targetChecksum = fs.existsSync(target) && !isSymlink(target) ? dirChecksum(target).checksum : null;
|
|
62
|
+
return {
|
|
63
|
+
source,
|
|
64
|
+
target,
|
|
65
|
+
sourceChecksum,
|
|
66
|
+
targetChecksum,
|
|
67
|
+
sourceVersion: source ? readArtifactVersion(source) : null,
|
|
68
|
+
targetVersion: targetChecksum ? readArtifactVersion(target) : null,
|
|
69
|
+
stale: !!source && !!sourceChecksum && !!targetChecksum && sourceChecksum !== targetChecksum,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 同步内置扩展产物到目标目录。
|
|
74
|
+
* - source 缺失(monorepo 未构建/npm 包缺资产)→ no-op
|
|
75
|
+
* - target 缺失 → 默认 no-op(首次安装走 hunter-mate install 引导);
|
|
76
|
+
* install 命令传 ensureTarget=true 创建
|
|
77
|
+
* - target 是软链(dev 模式)→ no-op,保护开发链
|
|
78
|
+
* - checksum 一致 → no-op;不一致 → 剪枝(删目标多余文件)+复制
|
|
79
|
+
*/
|
|
80
|
+
export function syncExtensionArtifacts(opts = {}) {
|
|
81
|
+
const nil = (reason) => ({ synced: false, reason, copied: 0, removed: 0 });
|
|
82
|
+
try {
|
|
83
|
+
const target = opts.target ?? defaultTargetDir();
|
|
84
|
+
const source = resolveBuiltinExtensionDist();
|
|
85
|
+
if (!source)
|
|
86
|
+
return nil('no-bundled-assets');
|
|
87
|
+
if (isSymlink(target))
|
|
88
|
+
return nil('target-is-symlink');
|
|
89
|
+
if (!fs.existsSync(target)) {
|
|
90
|
+
if (!opts.ensureTarget)
|
|
91
|
+
return nil('target-missing');
|
|
92
|
+
fs.mkdirSync(target, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
const cmp = compareExtensionArtifacts(target);
|
|
95
|
+
if (!cmp.stale) {
|
|
96
|
+
return { synced: false, reason: 'identical', copied: 0, removed: 0, fromVersion: cmp.targetVersion ?? undefined, toVersion: cmp.sourceVersion ?? undefined };
|
|
97
|
+
}
|
|
98
|
+
// 剪枝:目标中上游不存在的文件/空目录删除(纯复制会留下改名后的尸体文件)
|
|
99
|
+
let removed = 0;
|
|
100
|
+
const prune = (dst, src) => {
|
|
101
|
+
if (!fs.existsSync(dst))
|
|
102
|
+
return;
|
|
103
|
+
for (const name of fs.readdirSync(dst)) {
|
|
104
|
+
const d = path.join(dst, name);
|
|
105
|
+
const s = path.join(src, name);
|
|
106
|
+
if (!fs.existsSync(s)) {
|
|
107
|
+
fs.rmSync(d, { recursive: true, force: true });
|
|
108
|
+
removed++;
|
|
109
|
+
}
|
|
110
|
+
else if (fs.statSync(d).isDirectory()) {
|
|
111
|
+
prune(d, s);
|
|
112
|
+
if (fs.existsSync(d) && fs.readdirSync(d).length === 0)
|
|
113
|
+
fs.rmdirSync(d);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
prune(target, source);
|
|
118
|
+
// 复制(覆盖)
|
|
119
|
+
let copied = 0;
|
|
120
|
+
const copy = (src, dst) => {
|
|
121
|
+
const st = fs.statSync(src);
|
|
122
|
+
if (st.isDirectory()) {
|
|
123
|
+
if (!fs.existsSync(dst))
|
|
124
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
125
|
+
for (const f of fs.readdirSync(src))
|
|
126
|
+
copy(path.join(src, f), path.join(dst, f));
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
fs.copyFileSync(src, dst);
|
|
130
|
+
copied++;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
copy(source, target);
|
|
134
|
+
return {
|
|
135
|
+
synced: true,
|
|
136
|
+
reason: 'stale',
|
|
137
|
+
copied,
|
|
138
|
+
removed,
|
|
139
|
+
fromVersion: cmp.targetVersion ?? undefined,
|
|
140
|
+
toVersion: cmp.sourceVersion ?? undefined,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
// 永不抛出(postinstall 场景绝不能让 npm install 失败)
|
|
145
|
+
return { synced: false, reason: `error: ${err.message}`, copied: 0, removed: 0 };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function defaultTargetDir() {
|
|
149
|
+
const home = process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
|
|
150
|
+
return path.join(home, '.hunter-mate', 'extension');
|
|
151
|
+
}
|
|
152
|
+
function isSymlink(dir) {
|
|
153
|
+
try {
|
|
154
|
+
return fs.lstatSync(dir).isSymbolicLink();
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -59,5 +59,21 @@
|
|
|
59
59
|
"purge.done.next": "To re-initialize, run",
|
|
60
60
|
"purge.done.nothing": "Nothing to purge",
|
|
61
61
|
"login.serverOfflineSaasFallback": "Warning: local server is not running; logged in via SaaS directly. Start the server and run login again for local data operations.",
|
|
62
|
-
"login.serverOfflineReloginHint": "Note: the saved SaaS token will be rejected (401) by local server protected routes."
|
|
62
|
+
"login.serverOfflineReloginHint": "Note: the saved SaaS token will be rejected (401) by local server protected routes.",
|
|
63
|
+
"login.selfCheckOk": "Login self-check passed: protected endpoint reachable",
|
|
64
|
+
"login.selfCheckFail": "Login self-check failed: new token rejected by local server; please report with full output",
|
|
65
|
+
"login.selfCheckSkipped": "Login self-check skipped: local server unreachable; re-login once it is back",
|
|
66
|
+
"issueEnv.description": "Print the environment block needed for issue reports (CLI version / install type / git HEAD / server buildTime / OS); --format json for agents to paste directly",
|
|
67
|
+
"issueEnv.cliVersion": "CLI version",
|
|
68
|
+
"issueEnv.installType": "Install type",
|
|
69
|
+
"issueEnv.installDev": "dev (git worktree of source repo)",
|
|
70
|
+
"issueEnv.installPackage": "package (published npm build)",
|
|
71
|
+
"issueEnv.gitHead": "Code HEAD (version/buildTime do not identify code state; HEAD does)",
|
|
72
|
+
"issueEnv.gitBranch": "Branch",
|
|
73
|
+
"issueEnv.gitDirty": "uncommitted changes (dist may lag HEAD)",
|
|
74
|
+
"issueEnv.gitClean": "clean worktree",
|
|
75
|
+
"issueEnv.repoRoot": "Repo root",
|
|
76
|
+
"issueEnv.gitUnavailable": "unavailable (published package has no git info; use CLI version instead)",
|
|
77
|
+
"issueEnv.serverBuildTime": "server buildTime",
|
|
78
|
+
"issueEnv.serverNotRunning": "server not running (unavailable)"
|
|
63
79
|
}
|
|
@@ -59,5 +59,21 @@
|
|
|
59
59
|
"purge.done.next": "如需重新初始化,请运行",
|
|
60
60
|
"purge.done.nothing": "无需清理",
|
|
61
61
|
"login.serverOfflineSaasFallback": "警告: 本地 server 未运行,已直连 SaaS 登录;本地数据操作需启动 server 后重新 login。",
|
|
62
|
-
"login.serverOfflineReloginHint": "提示: 本次保存的是 SaaS 令牌,本地 server 的受保护路由将返回 401。"
|
|
62
|
+
"login.serverOfflineReloginHint": "提示: 本次保存的是 SaaS 令牌,本地 server 的受保护路由将返回 401。",
|
|
63
|
+
"login.selfCheckOk": "✓ 登录自检通过:受保护接口可达,search/report/job 可直接使用",
|
|
64
|
+
"login.selfCheckFail": "✗ 登录自检失败:新令牌被本地 server 拒绝,请带完整输出反馈,勿反复重试",
|
|
65
|
+
"login.selfCheckSkipped": "⚠ 登录自检跳过:本地 server 暂不可达,恢复后请重新 login",
|
|
66
|
+
"issueEnv.description": "输出提 issue 所需环境块(CLI 版本/安装方式/代码 git HEAD/server buildTime/OS),--format json 可供 agent 直接粘贴",
|
|
67
|
+
"issueEnv.cliVersion": "CLI 版本",
|
|
68
|
+
"issueEnv.installType": "安装方式",
|
|
69
|
+
"issueEnv.installDev": "dev(源码仓 git 工作区)",
|
|
70
|
+
"issueEnv.installPackage": "package(发布包)",
|
|
71
|
+
"issueEnv.gitHead": "代码 HEAD(版本号/buildTime 不说明代码状态,以 HEAD 为准)",
|
|
72
|
+
"issueEnv.gitBranch": "分支",
|
|
73
|
+
"issueEnv.gitDirty": "有未提交改动(dist 可能滞后于 HEAD)",
|
|
74
|
+
"issueEnv.gitClean": "工作区干净",
|
|
75
|
+
"issueEnv.repoRoot": "代码仓",
|
|
76
|
+
"issueEnv.gitUnavailable": "未获取(发布包无 git 信息,用 CLI 版本代替)",
|
|
77
|
+
"issueEnv.serverBuildTime": "server buildTime",
|
|
78
|
+
"issueEnv.serverNotRunning": "server 未运行(未获取)"
|
|
63
79
|
}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { extensionCommand } from './commands/extension.js';
|
|
|
14
14
|
import { reportCommand } from './commands/report.js';
|
|
15
15
|
import { recordCommand } from './commands/record.js';
|
|
16
16
|
import { cacheCommand } from './commands/cache.js';
|
|
17
|
+
import { migrateDataDirCommand } from './commands/migrate.js';
|
|
17
18
|
// 对应 ChromePilot 的新命令(环境管理 / 运维 / 扩展管理)
|
|
18
19
|
import { initCommand } from './commands/init.js';
|
|
19
20
|
import { installCommand } from './commands/install.js';
|
|
@@ -22,10 +23,18 @@ import { doctorCommand } from './commands/doctor.js';
|
|
|
22
23
|
import { reloadCommand, openExtCommand, purgeCommand } from './commands/extras.js';
|
|
23
24
|
import { recordingsCommand, adapterCommand, memoryCommand, traceCommand, autofixCommand } from './commands/ops.js';
|
|
24
25
|
import { pluginCommand, skillCommand, externalCommand } from './commands/addons.js';
|
|
26
|
+
import { customerCommand } from './commands/customer.js';
|
|
27
|
+
import { noteCommand } from './commands/note.js';
|
|
28
|
+
import { preferenceCommand } from './commands/preference.js';
|
|
29
|
+
import { caseCommand } from './commands/case.js';
|
|
30
|
+
import { issueEnvCommand } from './commands/issue-env.js';
|
|
25
31
|
import { i18n } from './i18n/index.js';
|
|
26
32
|
import { initCliLogger, flushCliLogger, getCliLogger } from './logger.js';
|
|
33
|
+
import { installLocalProxyBypass } from './utils/local-http.js';
|
|
27
34
|
import packageJson from '../package.json' with { type: 'json' };
|
|
28
35
|
const CLI_VERSION = packageJson.version;
|
|
36
|
+
// Issue #24:本地 server 探测强制绕过 http_proxy(仅 localhost/127.0.0.1,远程不动)
|
|
37
|
+
installLocalProxyBypass();
|
|
29
38
|
// 未捕获异常兜底(fix #27):任何命令抛出的未处理 Promise 统一友好提示 + exit 1,
|
|
30
39
|
// 不再裸奔 stack 或以 exit code 0 静默结束。
|
|
31
40
|
process.on('unhandledRejection', (reason) => {
|
|
@@ -81,6 +90,8 @@ program
|
|
|
81
90
|
const name = thisCommand.name();
|
|
82
91
|
if (name === 'update' || name === 'start' || name === 'stop')
|
|
83
92
|
return;
|
|
93
|
+
if (name === 'migrate-data-dir')
|
|
94
|
+
return; // 迁移过程中一致性检测/自更新会干扰移动与重启
|
|
84
95
|
if (name === 'doctor' || name === 'repair-linkedin-dupes')
|
|
85
96
|
return;
|
|
86
97
|
// fix #30: 命令以 --json(或 --format/-f json)输出时, [auto-update] 提示不得混入 stdout 的 JSON
|
|
@@ -160,4 +171,10 @@ program.addCommand(purgeCommand);
|
|
|
160
171
|
program.addCommand(reportCommand);
|
|
161
172
|
program.addCommand(recordCommand);
|
|
162
173
|
program.addCommand(cacheCommand);
|
|
174
|
+
program.addCommand(migrateDataDirCommand);
|
|
175
|
+
program.addCommand(customerCommand);
|
|
176
|
+
program.addCommand(noteCommand);
|
|
177
|
+
program.addCommand(preferenceCommand);
|
|
178
|
+
program.addCommand(caseCommand);
|
|
179
|
+
program.addCommand(issueEnvCommand);
|
|
163
180
|
program.parse();
|
package/dist/keychain.d.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* token 签发方(hmh#43):
|
|
3
|
+
* - local: 本地 server 自签 UTOKEN(设备码/邮箱代理路径),AuthMiddleware 可直接验签;
|
|
4
|
+
* - saas: 邮箱登录时本地 server 不可达,CLI 直连 SaaS 验证拿到的 token,
|
|
5
|
+
* 由 SaaS 密钥签发,本地 server 的 USER_SESSION_SECRET 验不过 → 数据面 401;
|
|
6
|
+
* - unknown: 旧版本存的裸 token(无 issuer 标记),无法区分签发方。
|
|
7
|
+
*/
|
|
8
|
+
export type TokenIssuer = 'local' | 'saas' | 'unknown';
|
|
9
|
+
export interface TokenInfo {
|
|
10
|
+
token: string;
|
|
11
|
+
issuer: TokenIssuer;
|
|
12
|
+
}
|
|
13
|
+
export declare function saveToken(token: string, issuer?: TokenIssuer): Promise<void>;
|
|
14
|
+
/** 只取 token 字符串(兼容全部历史调用方) */
|
|
2
15
|
export declare function getToken(): Promise<string | null>;
|
|
16
|
+
/** 取 token + 签发方标记(hmh#43 诊断用) */
|
|
17
|
+
export declare function getTokenInfo(): Promise<TokenInfo | null>;
|
|
3
18
|
export declare function deleteToken(): Promise<void>;
|
package/dist/keychain.js
CHANGED
|
@@ -1,30 +1,50 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import { resolveDataDir } from './config.js';
|
|
3
4
|
const SERVICE = 'hunter-mate';
|
|
4
5
|
const ACCOUNT = 'user';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/** token 落盘文件(FID-071:随 dataDir 配置迁移;keytar 可用时不使用) */
|
|
7
|
+
function tokenFile() {
|
|
8
|
+
return path.join(resolveDataDir(), 'token.json');
|
|
9
|
+
}
|
|
10
|
+
/** 兼容旧格式:裸 token 字符串或无 issuer 字段的 JSON */
|
|
11
|
+
function parseStored(raw) {
|
|
12
|
+
if (!raw)
|
|
13
|
+
return null;
|
|
14
|
+
try {
|
|
15
|
+
const data = JSON.parse(raw);
|
|
16
|
+
if (typeof data === 'string')
|
|
17
|
+
return { token: data, issuer: 'unknown' };
|
|
18
|
+
if (data && typeof data.token === 'string') {
|
|
19
|
+
const issuer = data.issuer === 'saas' || data.issuer === 'local' ? data.issuer : 'unknown';
|
|
20
|
+
return { token: data.token, issuer };
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return { token: raw, issuer: 'unknown' };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function fallbackSave(info) {
|
|
29
|
+
fs.mkdirSync(path.dirname(tokenFile()), { recursive: true });
|
|
9
30
|
// POSIX 上 mode 0o600 强制 owner-only;Windows NTFS 上 mode bit 被忽略,
|
|
10
31
|
// 由用户目录 ACL 兜底(%USERPROFILE%\.hunter-mate 默认只有当前用户可访问)。
|
|
11
32
|
// 真正安全的 Windows 路径是走 keytar(Windows Credential Manager)→ setPassword。
|
|
12
|
-
fs.writeFileSync(
|
|
33
|
+
fs.writeFileSync(tokenFile(), JSON.stringify(info), { mode: 0o600 });
|
|
13
34
|
}
|
|
14
35
|
function fallbackGet() {
|
|
15
|
-
if (!fs.existsSync(
|
|
36
|
+
if (!fs.existsSync(tokenFile()))
|
|
16
37
|
return null;
|
|
17
38
|
try {
|
|
18
|
-
|
|
19
|
-
return data.token || null;
|
|
39
|
+
return parseStored(fs.readFileSync(tokenFile(), 'utf-8'));
|
|
20
40
|
}
|
|
21
41
|
catch {
|
|
22
42
|
return null;
|
|
23
43
|
}
|
|
24
44
|
}
|
|
25
45
|
function fallbackDelete() {
|
|
26
|
-
if (fs.existsSync(
|
|
27
|
-
fs.unlinkSync(
|
|
46
|
+
if (fs.existsSync(tokenFile())) {
|
|
47
|
+
fs.unlinkSync(tokenFile());
|
|
28
48
|
}
|
|
29
49
|
}
|
|
30
50
|
async function getKeytar() {
|
|
@@ -41,26 +61,33 @@ async function getKeytar() {
|
|
|
41
61
|
return null;
|
|
42
62
|
}
|
|
43
63
|
}
|
|
44
|
-
export async function saveToken(token) {
|
|
64
|
+
export async function saveToken(token, issuer = 'local') {
|
|
65
|
+
const payload = JSON.stringify({ token, issuer });
|
|
45
66
|
const keytar = await getKeytar();
|
|
46
67
|
if (keytar) {
|
|
47
68
|
try {
|
|
48
|
-
await keytar.setPassword(SERVICE, ACCOUNT,
|
|
69
|
+
await keytar.setPassword(SERVICE, ACCOUNT, payload);
|
|
49
70
|
return;
|
|
50
71
|
}
|
|
51
72
|
catch {
|
|
52
73
|
// 降级到文件
|
|
53
74
|
}
|
|
54
75
|
}
|
|
55
|
-
fallbackSave(token);
|
|
76
|
+
fallbackSave({ token, issuer });
|
|
56
77
|
}
|
|
78
|
+
/** 只取 token 字符串(兼容全部历史调用方) */
|
|
57
79
|
export async function getToken() {
|
|
80
|
+
const info = await getTokenInfo();
|
|
81
|
+
return info?.token ?? null;
|
|
82
|
+
}
|
|
83
|
+
/** 取 token + 签发方标记(hmh#43 诊断用) */
|
|
84
|
+
export async function getTokenInfo() {
|
|
58
85
|
const keytar = await getKeytar();
|
|
59
86
|
if (keytar) {
|
|
60
87
|
try {
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
return
|
|
88
|
+
const raw = await keytar.getPassword(SERVICE, ACCOUNT);
|
|
89
|
+
if (raw)
|
|
90
|
+
return parseStored(raw);
|
|
64
91
|
}
|
|
65
92
|
catch {
|
|
66
93
|
// 降级
|
package/dist/logger.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Logger, createHttpTransport } from "@chromepilot/logger-kit/client";
|
|
2
2
|
import { PersistentLogQueue } from "@chromepilot/logger-kit/queue-node";
|
|
3
|
-
import { loadConfig } from "./config.js";
|
|
3
|
+
import { loadConfig, resolveDataDir } from "./config.js";
|
|
4
4
|
import { getToken } from "./keychain.js";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
let logger = null;
|
|
@@ -25,7 +25,7 @@ export function getCliLogger() {
|
|
|
25
25
|
return logger;
|
|
26
26
|
}
|
|
27
27
|
function homeDir() {
|
|
28
|
-
return
|
|
28
|
+
return resolveDataDir();
|
|
29
29
|
}
|
|
30
30
|
export async function initCliLogger(options) {
|
|
31
31
|
const cfg = loadConfig();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// postinstall.ts — npm install -g 后自动同步扩展产物到 ~/.hunter-mate/extension(Issue #23)
|
|
2
|
+
//
|
|
3
|
+
// 场景:用户 npm 升级 CLI(0.1.7 → 0.1.8+),~/.hunter-mate/extension 仍是旧版构建,
|
|
4
|
+
// update 一致性检查(运行中扩展 vs 磁盘)二者同为旧版,误判"无需 reload",
|
|
5
|
+
// 需手工 hunter-mate install 才刷新。postinstall 按 checksum 比对内置产物,
|
|
6
|
+
// 不一致即自动同步(等价自动跑 install 的产物同步部分)。
|
|
7
|
+
//
|
|
8
|
+
// 红线:本脚本在 npm install 事务内执行,任何情况不得非零退出(阻断用户安装)。
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import { syncExtensionArtifacts } from './extension-sync.js';
|
|
11
|
+
function main() {
|
|
12
|
+
// 未初始化过(~/.hunter-mate/extension 不存在)→ 首次安装走 hunter-mate install 引导,不抢跑
|
|
13
|
+
const home = process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE;
|
|
14
|
+
if (!home)
|
|
15
|
+
return;
|
|
16
|
+
const target = `${home}/.hunter-mate/extension`;
|
|
17
|
+
if (!fs.existsSync(target))
|
|
18
|
+
return;
|
|
19
|
+
const r = syncExtensionArtifacts();
|
|
20
|
+
if (r.synced) {
|
|
21
|
+
process.stdout.write(`[hunter-mate] 扩展产物已自动同步: ${r.fromVersion ?? '?'} → ${r.toVersion ?? '?'}`
|
|
22
|
+
+ `(copied=${r.copied}, removed=${r.removed})\n`);
|
|
23
|
+
process.stdout.write('[hunter-mate] 若扩展正在运行,请执行 hunter-mate reload 或在 chrome://extensions 刷新\n');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
main();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// 永不阻断 npm install
|
|
31
|
+
}
|
package/dist/updater.js
CHANGED
|
@@ -21,9 +21,9 @@ import { spawnSync } from 'node:child_process';
|
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
22
|
import { createCliUpdater } from '@chromepilot/kit';
|
|
23
23
|
import { readInstalledVersions } from './version.js';
|
|
24
|
+
import { resolveDataDir } from './config.js';
|
|
24
25
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
25
|
-
const
|
|
26
|
-
const ROOT = () => path.join(HOME(), '.hunter-mate');
|
|
26
|
+
const ROOT = resolveDataDir;
|
|
27
27
|
const CLI_PREFIX = () => path.join(ROOT(), 'cli');
|
|
28
28
|
/**
|
|
29
29
|
* 判定是否开发软链模式(纯函数,便于单测):
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// utils/artifact-checksum.ts — 构建产物目录指纹(sha1 + 最新 mtime)
|
|
2
|
+
// 从 version.ts 抽出:postinstall/extension-sync 等轻量入口也要用,
|
|
3
|
+
// 不能反向依赖 version.ts(它拉 axios/nestjs 一整条重链)。
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { createHash } from 'crypto';
|
|
7
|
+
export function dirChecksum(dir) {
|
|
8
|
+
const hash = createHash('sha1');
|
|
9
|
+
let latest = 0;
|
|
10
|
+
function walk(d, rel = '') {
|
|
11
|
+
try {
|
|
12
|
+
const entries = fs.readdirSync(d, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
const relPath = path.join(rel, entry.name);
|
|
15
|
+
const full = path.join(d, entry.name);
|
|
16
|
+
if (entry.isDirectory()) {
|
|
17
|
+
walk(full, relPath);
|
|
18
|
+
}
|
|
19
|
+
else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
20
|
+
try {
|
|
21
|
+
const stat = fs.statSync(full);
|
|
22
|
+
if (stat.mtimeMs > latest)
|
|
23
|
+
latest = stat.mtimeMs;
|
|
24
|
+
hash.update(relPath + '\0');
|
|
25
|
+
const buf = fs.readFileSync(full);
|
|
26
|
+
hash.update(buf);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
/* ignore unreadable */
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
/* dir missing → empty */
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
walk(dir);
|
|
39
|
+
return { checksum: hash.digest('hex'), latestMtimeMs: latest };
|
|
40
|
+
}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* 命令错误统一出口(对应 fix #18/#28):
|
|
3
3
|
* - logger 留痕(后台可检索,与现有 getCliLogger().error 一致)
|
|
4
4
|
* - console.error 落终端(用户能直接看懂,不再静默 exit 1)
|
|
5
|
-
* - 401/403
|
|
5
|
+
* - 401/403 时给出重新登录的中文提示;
|
|
6
|
+
* hmh#43:token 由 SaaS 签发(本地 server 未运行时登录所致)时给出定向诊断,
|
|
7
|
+
* 不再笼统报"登录已过期"
|
|
6
8
|
*
|
|
7
9
|
* 调用方随后自行 process.exit(1)。
|
|
8
10
|
*/
|
|
9
|
-
export declare function printCommandError(scope: string, err: unknown): void
|
|
11
|
+
export declare function printCommandError(scope: string, err: unknown): Promise<void>;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { getCliLogger } from '../logger.js';
|
|
2
|
+
import { getTokenInfo } from '../keychain.js';
|
|
2
3
|
/**
|
|
3
4
|
* 命令错误统一出口(对应 fix #18/#28):
|
|
4
5
|
* - logger 留痕(后台可检索,与现有 getCliLogger().error 一致)
|
|
5
6
|
* - console.error 落终端(用户能直接看懂,不再静默 exit 1)
|
|
6
|
-
* - 401/403
|
|
7
|
+
* - 401/403 时给出重新登录的中文提示;
|
|
8
|
+
* hmh#43:token 由 SaaS 签发(本地 server 未运行时登录所致)时给出定向诊断,
|
|
9
|
+
* 不再笼统报"登录已过期"
|
|
7
10
|
*
|
|
8
11
|
* 调用方随后自行 process.exit(1)。
|
|
9
12
|
*/
|
|
10
|
-
export function printCommandError(scope, err) {
|
|
13
|
+
export async function printCommandError(scope, err) {
|
|
11
14
|
const e = err;
|
|
12
15
|
const status = e?.response?.status;
|
|
13
16
|
const body = e?.response?.data;
|
|
@@ -21,7 +24,20 @@ export function printCommandError(scope, err) {
|
|
|
21
24
|
}
|
|
22
25
|
if (status === 401 || status === 403) {
|
|
23
26
|
console.error(`✗ ${message} (HTTP ${status})`);
|
|
24
|
-
|
|
27
|
+
let issuer = 'unknown';
|
|
28
|
+
try {
|
|
29
|
+
issuer = (await getTokenInfo())?.issuer ?? 'unknown';
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// keychain 不可用时按 unknown 处理
|
|
33
|
+
}
|
|
34
|
+
if (issuer === 'saas') {
|
|
35
|
+
console.error('当前 token 由 SaaS 签发(登录时本地 server 未运行),本地 server 拒绝使用。\n' +
|
|
36
|
+
'请先启动本地 server(hunter-mate start),再重新执行 hunter-mate login。');
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.error('登录已过期或无权访问,请运行 hunter-mate login 后重试');
|
|
40
|
+
}
|
|
25
41
|
return;
|
|
26
42
|
}
|
|
27
43
|
console.error(`✗ ${scope}: ${message}${status ? ` (HTTP ${status})` : ''}`);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type LinkKind = 'dir' | 'file';
|
|
2
|
+
export interface LinkResult {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
fallback?: 'copy+marker';
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* linkDev: cross-platform "point at the build artifact" dev-link helper.
|
|
9
|
+
* 实现对齐 chromepilot packages/cli/src/utils/link.ts(SSOT,同构复制)。
|
|
10
|
+
*
|
|
11
|
+
* - POSIX: fs.symlinkSync(target, linkPath, type)
|
|
12
|
+
* - Windows directory: NTFS junction (no admin needed; cannot cross drives)。
|
|
13
|
+
* 跨盘或 junction 失败退化 copy + `<linkPath>.devlink` marker。
|
|
14
|
+
* - Windows file: copy + marker(Windows 文件软链需要 SeCreateSymbolicLink 权限,避免使用)。
|
|
15
|
+
*/
|
|
16
|
+
export declare function linkDev(target: string, linkPath: string, kind?: LinkKind): LinkResult;
|
|
17
|
+
/** lstat 对悬空软链会抛异常而不是返回 false;包一层保证调用方拿到 boolean */
|
|
18
|
+
export declare function isSymbolicLinkSafe(p: string): boolean;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* linkDev: cross-platform "point at the build artifact" dev-link helper.
|
|
5
|
+
* 实现对齐 chromepilot packages/cli/src/utils/link.ts(SSOT,同构复制)。
|
|
6
|
+
*
|
|
7
|
+
* - POSIX: fs.symlinkSync(target, linkPath, type)
|
|
8
|
+
* - Windows directory: NTFS junction (no admin needed; cannot cross drives)。
|
|
9
|
+
* 跨盘或 junction 失败退化 copy + `<linkPath>.devlink` marker。
|
|
10
|
+
* - Windows file: copy + marker(Windows 文件软链需要 SeCreateSymbolicLink 权限,避免使用)。
|
|
11
|
+
*/
|
|
12
|
+
export function linkDev(target, linkPath, kind = 'dir') {
|
|
13
|
+
if (fs.existsSync(linkPath)) {
|
|
14
|
+
try {
|
|
15
|
+
fs.rmSync(linkPath, { recursive: true, force: true });
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
const e = err;
|
|
19
|
+
return { ok: false, error: `清理已存在目标失败: ${e.message}` };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (process.platform !== 'win32') {
|
|
23
|
+
try {
|
|
24
|
+
fs.symlinkSync(target, linkPath, kind);
|
|
25
|
+
return { ok: true };
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
const e = err;
|
|
29
|
+
return { ok: false, error: `symlink 失败: ${e.message}` };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Windows + directory: try junction
|
|
33
|
+
if (kind === 'dir') {
|
|
34
|
+
const linkDrive = path.parse(path.resolve(linkPath)).root;
|
|
35
|
+
const targetDrive = path.parse(path.resolve(target)).root;
|
|
36
|
+
if (linkDrive && targetDrive && linkDrive !== targetDrive) {
|
|
37
|
+
try {
|
|
38
|
+
fs.cpSync(target, linkPath, { recursive: true });
|
|
39
|
+
fs.writeFileSync(`${linkPath}.devlink`, path.resolve(target), 'utf-8');
|
|
40
|
+
return { ok: true, fallback: 'copy+marker' };
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const e = err;
|
|
44
|
+
return { ok: false, error: `junction 跨盘符退化失败: ${e.message}` };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
fs.symlinkSync(target, linkPath, 'junction');
|
|
49
|
+
return { ok: true };
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const e = err;
|
|
53
|
+
try {
|
|
54
|
+
fs.cpSync(target, linkPath, { recursive: true });
|
|
55
|
+
fs.writeFileSync(`${linkPath}.devlink`, path.resolve(target), 'utf-8');
|
|
56
|
+
return { ok: true, fallback: 'copy+marker' };
|
|
57
|
+
}
|
|
58
|
+
catch (err2) {
|
|
59
|
+
const e2 = err2;
|
|
60
|
+
return { ok: false, error: `junction 与 copy 兜底均失败: ${e.message} / ${e2.message}` };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Windows + file: copy + marker
|
|
65
|
+
try {
|
|
66
|
+
fs.copyFileSync(target, linkPath);
|
|
67
|
+
fs.writeFileSync(`${linkPath}.devlink`, path.resolve(target), 'utf-8');
|
|
68
|
+
return { ok: true, fallback: 'copy+marker' };
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
const e = err;
|
|
72
|
+
return { ok: false, error: `copy+marker 失败: ${e.message}` };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** lstat 对悬空软链会抛异常而不是返回 false;包一层保证调用方拿到 boolean */
|
|
76
|
+
export function isSymbolicLinkSafe(p) {
|
|
77
|
+
try {
|
|
78
|
+
return fs.lstatSync(p).isSymbolicLink();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function isLocalServerUrl(url: string | undefined): boolean;
|
|
2
|
+
/**
|
|
3
|
+
* 全局 axios 请求拦截器:调用方未显式指定 proxy 且目标是本地地址时强制 proxy:false。
|
|
4
|
+
* 在 CLI 入口(index.ts)注册一次,覆盖 status/doctor/extension 等所有探测路径;
|
|
5
|
+
* 显式传了 proxy(含已有的 proxy:false)的请求不覆盖,尊重调用方意图。
|
|
6
|
+
*/
|
|
7
|
+
export declare function installLocalProxyBypass(): void;
|