hunter-mate 0.1.0
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/cache/db.d.ts +7 -0
- package/dist/cache/db.js +105 -0
- package/dist/commands/addons.d.ts +4 -0
- package/dist/commands/addons.js +378 -0
- package/dist/commands/cache.d.ts +8 -0
- package/dist/commands/cache.js +58 -0
- package/dist/commands/config.d.ts +12 -0
- package/dist/commands/config.js +27 -0
- package/dist/commands/doctor.d.ts +21 -0
- package/dist/commands/doctor.js +332 -0
- package/dist/commands/extension.d.ts +5 -0
- package/dist/commands/extension.js +438 -0
- package/dist/commands/extras.d.ts +10 -0
- package/dist/commands/extras.js +111 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.js +87 -0
- package/dist/commands/install.d.ts +9 -0
- package/dist/commands/install.js +126 -0
- package/dist/commands/job.d.ts +9 -0
- package/dist/commands/job.js +114 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +51 -0
- package/dist/commands/logout.d.ts +3 -0
- package/dist/commands/logout.js +16 -0
- package/dist/commands/native-check.d.ts +22 -0
- package/dist/commands/native-check.js +93 -0
- package/dist/commands/ops.d.ts +6 -0
- package/dist/commands/ops.js +330 -0
- package/dist/commands/record.d.ts +12 -0
- package/dist/commands/record.js +96 -0
- package/dist/commands/report.d.ts +7 -0
- package/dist/commands/report.js +54 -0
- package/dist/commands/search.d.ts +14 -0
- package/dist/commands/search.js +46 -0
- package/dist/commands/server.d.ts +23 -0
- package/dist/commands/server.js +232 -0
- package/dist/commands/status.d.ts +20 -0
- package/dist/commands/status.js +92 -0
- package/dist/commands/update.d.ts +8 -0
- package/dist/commands/update.js +161 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.js +65 -0
- package/dist/i18n/index.d.ts +7 -0
- package/dist/i18n/index.js +24 -0
- package/dist/i18n/locales/en-US.json +35 -0
- package/dist/i18n/locales/zh-CN.json +35 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +133 -0
- package/dist/keychain.d.ts +3 -0
- package/dist/keychain.js +117 -0
- package/dist/sentry.d.ts +3 -0
- package/dist/sentry.js +62 -0
- package/dist/updater.d.ts +23 -0
- package/dist/updater.js +161 -0
- package/dist/version.d.ts +16 -0
- package/dist/version.js +249 -0
- package/package.json +50 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
export declare function ensureCacheDir(): void;
|
|
3
|
+
export declare function getDb(): Database.Database;
|
|
4
|
+
export declare function saveJobCase(jobCase: any): void;
|
|
5
|
+
export declare function listJobCases(): any[];
|
|
6
|
+
export declare function saveCommunicationRecord(record: any): void;
|
|
7
|
+
export declare function listCommunicationRecords(): any[];
|
package/dist/cache/db.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.ensureCacheDir = ensureCacheDir;
|
|
40
|
+
exports.getDb = getDb;
|
|
41
|
+
exports.saveJobCase = saveJobCase;
|
|
42
|
+
exports.listJobCases = listJobCases;
|
|
43
|
+
exports.saveCommunicationRecord = saveCommunicationRecord;
|
|
44
|
+
exports.listCommunicationRecords = listCommunicationRecords;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
48
|
+
const HOME = process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
|
|
49
|
+
const CACHE_DIR = path.join(HOME, '.hunter-mate');
|
|
50
|
+
const DB_FILE = path.join(CACHE_DIR, 'cache.db');
|
|
51
|
+
let db = null;
|
|
52
|
+
function ensureCacheDir() {
|
|
53
|
+
if (!fs.existsSync(CACHE_DIR)) {
|
|
54
|
+
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function getDb() {
|
|
58
|
+
if (!db) {
|
|
59
|
+
ensureCacheDir();
|
|
60
|
+
db = new better_sqlite3_1.default(DB_FILE);
|
|
61
|
+
initSchema(db);
|
|
62
|
+
}
|
|
63
|
+
return db;
|
|
64
|
+
}
|
|
65
|
+
function initSchema(db) {
|
|
66
|
+
db.exec(`
|
|
67
|
+
CREATE TABLE IF NOT EXISTS job_cases (
|
|
68
|
+
id TEXT PRIMARY KEY,
|
|
69
|
+
payload TEXT NOT NULL,
|
|
70
|
+
created_at INTEGER NOT NULL
|
|
71
|
+
);
|
|
72
|
+
CREATE TABLE IF NOT EXISTS communication_records (
|
|
73
|
+
id TEXT PRIMARY KEY,
|
|
74
|
+
payload TEXT NOT NULL,
|
|
75
|
+
created_at INTEGER NOT NULL
|
|
76
|
+
);
|
|
77
|
+
CREATE TABLE IF NOT EXISTS search_history (
|
|
78
|
+
id TEXT PRIMARY KEY,
|
|
79
|
+
query TEXT,
|
|
80
|
+
filter_json TEXT,
|
|
81
|
+
result_count INTEGER,
|
|
82
|
+
created_at INTEGER
|
|
83
|
+
);
|
|
84
|
+
`);
|
|
85
|
+
}
|
|
86
|
+
function saveJobCase(jobCase) {
|
|
87
|
+
const stmt = getDb().prepare('INSERT OR REPLACE INTO job_cases (id, payload, created_at) VALUES (?, ?, ?)');
|
|
88
|
+
stmt.run(jobCase.id, JSON.stringify(jobCase), jobCase.createdAt);
|
|
89
|
+
}
|
|
90
|
+
function listJobCases() {
|
|
91
|
+
return getDb()
|
|
92
|
+
.prepare('SELECT payload FROM job_cases ORDER BY created_at DESC')
|
|
93
|
+
.all()
|
|
94
|
+
.map((row) => JSON.parse(row.payload));
|
|
95
|
+
}
|
|
96
|
+
function saveCommunicationRecord(record) {
|
|
97
|
+
const stmt = getDb().prepare('INSERT OR REPLACE INTO communication_records (id, payload, created_at) VALUES (?, ?, ?)');
|
|
98
|
+
stmt.run(String(record.id), JSON.stringify(record), Date.parse(record.createdAt) || Date.now());
|
|
99
|
+
}
|
|
100
|
+
function listCommunicationRecords() {
|
|
101
|
+
return getDb()
|
|
102
|
+
.prepare('SELECT payload FROM communication_records ORDER BY created_at DESC')
|
|
103
|
+
.all()
|
|
104
|
+
.map((row) => JSON.parse(row.payload));
|
|
105
|
+
}
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.externalCommand = exports.skillCommand = exports.pluginCommand = void 0;
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
function formatOpt(cmd) {
|
|
41
|
+
return cmd.option('-f, --format <format>', '输出格式: table|json|yaml|md|csv', 'json');
|
|
42
|
+
}
|
|
43
|
+
const printResult = (data, format) => {
|
|
44
|
+
const fmt = (format ?? 'json').toLowerCase();
|
|
45
|
+
if (fmt === 'json') {
|
|
46
|
+
console.log(typeof data === 'string' ? data : JSON.stringify(data, null, 2));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (fmt === 'table') {
|
|
50
|
+
if (Array.isArray(data))
|
|
51
|
+
console.table(data);
|
|
52
|
+
else if (data && typeof data === 'object')
|
|
53
|
+
console.table([data]);
|
|
54
|
+
else
|
|
55
|
+
console.log(String(data));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
console.log(typeof data === 'string' ? data : JSON.stringify(data, null, 2));
|
|
59
|
+
};
|
|
60
|
+
const ROOT = () => path.join(process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.', '.hunter-mate');
|
|
61
|
+
const PLUGINS_DIR = () => path.join(ROOT(), 'plugins');
|
|
62
|
+
const SKILLS_DIR = () => path.join(ROOT(), 'skills');
|
|
63
|
+
const EXTERNAL_FILE = () => path.join(ROOT(), 'externals.json');
|
|
64
|
+
const ensure = (d) => { if (!fs.existsSync(d))
|
|
65
|
+
fs.mkdirSync(d, { recursive: true }); };
|
|
66
|
+
const readJson = (f, fallback) => {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(fs.readFileSync(f, 'utf-8'));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return fallback;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const writeJson = (f, v) => fs.writeFileSync(f, JSON.stringify(v, null, 2));
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// plugin (对应 chromepilot plugin)
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
exports.pluginCommand = formatOpt(new commander_1.Command('plugin')
|
|
79
|
+
.description('插件管理(对应 chromepilot plugin): list|install|update|uninstall|create')
|
|
80
|
+
.argument('[action]', '操作 (默认 list)', 'list')
|
|
81
|
+
.argument('[arg]', 'install:source / uninstall|update:name / create:name')).action(async (action, arg, opts) => {
|
|
82
|
+
ensure(PLUGINS_DIR());
|
|
83
|
+
// install/update/uninstall 后: server 在跑则自动热重载插件(磁盘 vs 运行版本对比)
|
|
84
|
+
const syncPlugins = () => Promise.resolve().then(() => __importStar(require('../version'))).then(({ ensureRuntimeConsistency }) => ensureRuntimeConsistency({ force: true, verbose: true }))
|
|
85
|
+
.catch(() => { });
|
|
86
|
+
switch (action) {
|
|
87
|
+
case 'list': {
|
|
88
|
+
const dirs = fs.existsSync(PLUGINS_DIR()) ? fs.readdirSync(PLUGINS_DIR()) : [];
|
|
89
|
+
const rows = dirs.map((name) => {
|
|
90
|
+
const mf = path.join(PLUGINS_DIR(), name, 'package.json');
|
|
91
|
+
let version = '?';
|
|
92
|
+
let desc = '';
|
|
93
|
+
try {
|
|
94
|
+
const p = JSON.parse(fs.readFileSync(mf, 'utf-8'));
|
|
95
|
+
version = p.version ?? '?';
|
|
96
|
+
desc = p.description ?? '';
|
|
97
|
+
}
|
|
98
|
+
catch { }
|
|
99
|
+
return { name, version, description: desc, path: path.join(PLUGINS_DIR(), name) };
|
|
100
|
+
});
|
|
101
|
+
printResult(rows, opts.format);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
case 'create': {
|
|
105
|
+
if (!arg) {
|
|
106
|
+
console.error('plugin create 需要 name');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
const dir = path.join(PLUGINS_DIR(), arg);
|
|
110
|
+
if (fs.existsSync(dir)) {
|
|
111
|
+
console.error(`plugin ${arg} already exists: ${dir}`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
ensure(dir);
|
|
115
|
+
writeJson(path.join(dir, 'package.json'), {
|
|
116
|
+
name: `@hunter-mate/plugin-${arg}`,
|
|
117
|
+
version: '0.1.0',
|
|
118
|
+
description: `HunterMate plugin: ${arg}`,
|
|
119
|
+
main: 'index.js',
|
|
120
|
+
});
|
|
121
|
+
fs.writeFileSync(path.join(dir, 'index.js'), `// plugin "${arg}" entry — export hooks: controlMiddleware / beforeCommand / afterCommand / eventHandler\nmodule.exports = { name: ${JSON.stringify(arg)} };\n`);
|
|
122
|
+
printResult({ ok: true, created: dir, next: [
|
|
123
|
+
`把自定义命令注册到 package.json -> hunter-mate.commands;`,
|
|
124
|
+
`通过 \`hunter-mate plugin install <local-path|npm-pkg>\` 启用;`,
|
|
125
|
+
`钩子在控制命令执行前后自动被 CLI/Server 加载。`,
|
|
126
|
+
] }, opts.format);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
case 'install': {
|
|
130
|
+
if (!arg) {
|
|
131
|
+
console.error('plugin install 需要 source(本地路径或 npm 包名)');
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
// MVP: 本地路径复制;npm 包需单独调用 npm install --prefix
|
|
135
|
+
const abs = path.resolve(arg);
|
|
136
|
+
if (!fs.existsSync(abs)) {
|
|
137
|
+
printResult({ ok: false, error: `source not found: ${abs}`, hint: `对于 npm 包:先 cd ~/.hunter-mate/plugins && npm install ${arg}` }, opts.format);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const name = path.basename(abs).replace(/^@/, '').replace(/\//, '-');
|
|
141
|
+
const dst = path.join(PLUGINS_DIR(), name);
|
|
142
|
+
fs.cpSync(abs, dst, { recursive: true });
|
|
143
|
+
await syncPlugins();
|
|
144
|
+
printResult({ ok: true, from: abs, installedTo: dst }, opts.format);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
case 'update': {
|
|
148
|
+
if (!arg) {
|
|
149
|
+
console.error('plugin update 需要 name');
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
const dir = path.join(PLUGINS_DIR(), arg);
|
|
153
|
+
if (!fs.existsSync(dir)) {
|
|
154
|
+
printResult({ ok: false, error: `plugin not found: ${arg}` }, opts.format);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// 真实更新: git 仓 → git pull;npm 包 → npm update;完成后触发 server 端热重载
|
|
158
|
+
const { spawnSync } = require('child_process');
|
|
159
|
+
let updated = false;
|
|
160
|
+
let detail = '';
|
|
161
|
+
if (fs.existsSync(path.join(dir, '.git'))) {
|
|
162
|
+
const r = spawnSync('git', ['-C', dir, 'pull', '--ff-only'], { encoding: 'utf-8', timeout: 60_000 });
|
|
163
|
+
updated = r.status === 0;
|
|
164
|
+
detail = r.stdout?.trim() || r.stderr?.trim() || '';
|
|
165
|
+
}
|
|
166
|
+
else if (fs.existsSync(path.join(dir, 'package.json')) && fs.existsSync(path.join(dir, 'node_modules'))) {
|
|
167
|
+
const r = spawnSync('npm', ['update', '--prefix', dir], { encoding: 'utf-8', timeout: 120_000 });
|
|
168
|
+
updated = r.status === 0;
|
|
169
|
+
detail = r.stdout?.trim() || r.stderr?.trim() || '';
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
detail = `非 git 仓且非 npm 安装目录,请手动更新: ${dir}`;
|
|
173
|
+
}
|
|
174
|
+
// 更新后: server 在跑则热重载该插件(ensureRuntimeConsistency 会对比磁盘 vs 运行版本)
|
|
175
|
+
await syncPlugins();
|
|
176
|
+
printResult({ ok: updated, name: arg, updated, detail, next: '若 server 正在运行,插件将自动热重载' }, opts.format);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
case 'uninstall': {
|
|
180
|
+
if (!arg) {
|
|
181
|
+
console.error('plugin uninstall 需要 name');
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
const dir = path.join(PLUGINS_DIR(), arg);
|
|
185
|
+
if (!fs.existsSync(dir)) {
|
|
186
|
+
printResult({ ok: false, error: `plugin not found: ${arg}` }, opts.format);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
190
|
+
await syncPlugins();
|
|
191
|
+
printResult({ ok: true, removed: dir }, opts.format);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
default:
|
|
195
|
+
console.error(`plugin: unknown action ${action}(支持: list|install|update|uninstall|create)`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// skill (对应 chromepilot skill)
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
exports.skillCommand = formatOpt(new commander_1.Command('skill')
|
|
203
|
+
.description('本地 CLI 级 skill 管理(对应 chromepilot skill): list|show|path|create|remove|edit')
|
|
204
|
+
.argument('[action]', '操作 (默认 list)', 'list')
|
|
205
|
+
.argument('[name]', 'skill 名称')).action(async (action, name, opts) => {
|
|
206
|
+
ensure(SKILLS_DIR());
|
|
207
|
+
switch (action) {
|
|
208
|
+
case 'list': {
|
|
209
|
+
const dirs = fs.existsSync(SKILLS_DIR()) ? fs.readdirSync(SKILLS_DIR()) : [];
|
|
210
|
+
const rows = dirs.map((n) => {
|
|
211
|
+
const sm = path.join(SKILLS_DIR(), n, 'SKILL.md');
|
|
212
|
+
return { name, hasSkillMd: fs.existsSync(sm), path: path.join(SKILLS_DIR(), n) };
|
|
213
|
+
});
|
|
214
|
+
printResult(rows, opts.format);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
case 'path': {
|
|
218
|
+
if (!name) {
|
|
219
|
+
console.error('skill path 需要 name');
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
const p = path.join(SKILLS_DIR(), name);
|
|
223
|
+
printResult({ name, path: p }, opts.format);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
case 'show': {
|
|
227
|
+
if (!name) {
|
|
228
|
+
console.error('skill show 需要 name');
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
const sm = path.join(SKILLS_DIR(), name, 'SKILL.md');
|
|
232
|
+
if (!fs.existsSync(sm)) {
|
|
233
|
+
printResult({ name, error: 'SKILL.md 不存在' }, opts.format);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
console.log(fs.readFileSync(sm, 'utf-8'));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
case 'create': {
|
|
240
|
+
if (!name) {
|
|
241
|
+
console.error('skill create 需要 name');
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
const dir = path.join(SKILLS_DIR(), name);
|
|
245
|
+
if (fs.existsSync(dir)) {
|
|
246
|
+
console.error(`skill ${name} 已存在`);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
ensure(dir);
|
|
250
|
+
fs.writeFileSync(path.join(dir, 'SKILL.md'), [
|
|
251
|
+
'---',
|
|
252
|
+
`name: ${name}`,
|
|
253
|
+
`description: TODO: 一句话描述 skill 做什么`,
|
|
254
|
+
`version: 0.1.0`,
|
|
255
|
+
'---',
|
|
256
|
+
'',
|
|
257
|
+
`# ${name}`,
|
|
258
|
+
'',
|
|
259
|
+
'## 流程',
|
|
260
|
+
'- TODO: 把操作步骤写在这里',
|
|
261
|
+
'',
|
|
262
|
+
'## 禁止',
|
|
263
|
+
'- TODO: 列出该 skill 禁止做的事(越权/隐私/污染数据等)',
|
|
264
|
+
'',
|
|
265
|
+
].join('\n'));
|
|
266
|
+
printResult({ ok: true, created: dir }, opts.format);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
case 'remove': {
|
|
270
|
+
if (!name) {
|
|
271
|
+
console.error('skill remove 需要 name');
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
const dir = path.join(SKILLS_DIR(), name);
|
|
275
|
+
if (!fs.existsSync(dir)) {
|
|
276
|
+
printResult({ ok: false, error: `skill not found: ${name}` }, opts.format);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
280
|
+
printResult({ ok: true, removed: dir }, opts.format);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
case 'edit': {
|
|
284
|
+
if (!name) {
|
|
285
|
+
console.error('skill edit 需要 name');
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
const sm = path.join(SKILLS_DIR(), name, 'SKILL.md');
|
|
289
|
+
try {
|
|
290
|
+
const { default: open } = await Promise.resolve().then(() => __importStar(require('open')));
|
|
291
|
+
await open(sm);
|
|
292
|
+
printResult({ ok: true, opening: sm }, opts.format);
|
|
293
|
+
}
|
|
294
|
+
catch (e) {
|
|
295
|
+
console.warn(`open not available: ${e.message}. 手动编辑: ${sm}`);
|
|
296
|
+
}
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
default:
|
|
300
|
+
console.error(`skill: unknown action ${action}(支持: list|show|path|create|remove|edit)`);
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
// ---------------------------------------------------------------------------
|
|
305
|
+
// external (对应 chromepilot external)
|
|
306
|
+
// ---------------------------------------------------------------------------
|
|
307
|
+
exports.externalCommand = formatOpt(new commander_1.Command('external')
|
|
308
|
+
.description('外部 CLI 透传(对应 chromepilot external): list|register|install|run|unregister')
|
|
309
|
+
.argument('[action]', '操作 (默认 list)', 'list')
|
|
310
|
+
.argument('[name]', 'CLI 名称'))
|
|
311
|
+
.option('--binary <path>', 'register: CLI 二进制路径')
|
|
312
|
+
.option('--desc <desc>', 'register: CLI 描述')
|
|
313
|
+
.action((action, name, opts) => {
|
|
314
|
+
ensure(ROOT());
|
|
315
|
+
const store = readJson(EXTERNAL_FILE(), { entries: [] });
|
|
316
|
+
switch (action) {
|
|
317
|
+
case 'list':
|
|
318
|
+
printResult(store.entries, opts.format);
|
|
319
|
+
return;
|
|
320
|
+
case 'register': {
|
|
321
|
+
if (!name || !opts.binary) {
|
|
322
|
+
console.error('external register 需要 name + --binary <path>');
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
const e = { name, binary: opts.binary, description: opts.desc, registeredAt: new Date().toISOString() };
|
|
326
|
+
store.entries = store.entries.filter((x) => x.name !== name).concat(e);
|
|
327
|
+
writeJson(EXTERNAL_FILE(), store);
|
|
328
|
+
printResult({ ok: true, registered: e }, opts.format);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
case 'unregister': {
|
|
332
|
+
if (!name) {
|
|
333
|
+
console.error('external unregister 需要 name');
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
store.entries = store.entries.filter((x) => x.name !== name);
|
|
337
|
+
writeJson(EXTERNAL_FILE(), store);
|
|
338
|
+
printResult({ ok: true, removed: name }, opts.format);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
case 'install': {
|
|
342
|
+
if (!name) {
|
|
343
|
+
console.error('external install 需要 name(已注册的 CLI)');
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
const e = store.entries.find((x) => x.name === name);
|
|
347
|
+
if (!e) {
|
|
348
|
+
console.error(`external not registered: ${name}`);
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
// 校验可执行权限/存在
|
|
352
|
+
const exists = fs.existsSync(e.binary);
|
|
353
|
+
printResult({ ok: exists, name, binary: e.binary, executable: exists && (fs.constants?.X_OK === undefined || (() => { try {
|
|
354
|
+
fs.accessSync(e.binary, fs.constants.X_OK);
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
return false;
|
|
359
|
+
} })()) }, opts.format);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
case 'run': {
|
|
363
|
+
if (!name) {
|
|
364
|
+
console.error('external run 需要 name');
|
|
365
|
+
process.exit(1);
|
|
366
|
+
}
|
|
367
|
+
const e = store.entries.find((x) => x.name === name);
|
|
368
|
+
if (!e) {
|
|
369
|
+
console.error(`external not registered: ${name}. 先 hunter-mate external register ${name} --binary <path>`);
|
|
370
|
+
process.exit(1);
|
|
371
|
+
}
|
|
372
|
+
const { spawnSync } = require('child_process');
|
|
373
|
+
const extraArgs = process.argv.slice(process.argv.indexOf(name) + 1);
|
|
374
|
+
const r = spawnSync(e.binary, extraArgs, { stdio: 'inherit', shell: false });
|
|
375
|
+
process.exit(r.status ?? 0);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export interface CacheSyncResult {
|
|
3
|
+
jobCaseCount: number;
|
|
4
|
+
communicationCount: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function runCacheSync(): Promise<CacheSyncResult>;
|
|
7
|
+
export declare function runCacheClear(): void;
|
|
8
|
+
export declare const cacheCommand: Command;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.cacheCommand = void 0;
|
|
7
|
+
exports.runCacheSync = runCacheSync;
|
|
8
|
+
exports.runCacheClear = runCacheClear;
|
|
9
|
+
const commander_1 = require("commander");
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
11
|
+
const config_1 = require("../config");
|
|
12
|
+
const keychain_1 = require("../keychain");
|
|
13
|
+
const db_1 = require("../cache/db");
|
|
14
|
+
async function runCacheSync() {
|
|
15
|
+
const config = (0, config_1.loadConfig)();
|
|
16
|
+
const token = await (0, keychain_1.getToken)();
|
|
17
|
+
const headers = token ? { Authorization: `UTOKEN ${token}` } : {};
|
|
18
|
+
(0, db_1.ensureCacheDir)();
|
|
19
|
+
const [jobRes, commRes] = await Promise.all([
|
|
20
|
+
axios_1.default.get(`${config.serverUrl}/job-cases`, { headers, proxy: false }),
|
|
21
|
+
axios_1.default.get(`${config.serverUrl}/communication-records`, { headers, proxy: false }),
|
|
22
|
+
]);
|
|
23
|
+
const jobCases = jobRes.data || [];
|
|
24
|
+
const records = commRes.data || [];
|
|
25
|
+
for (const jc of jobCases) {
|
|
26
|
+
(0, db_1.saveJobCase)({ ...jc, createdAt: Date.parse(jc.createdAt) || Date.now() });
|
|
27
|
+
}
|
|
28
|
+
for (const r of records) {
|
|
29
|
+
(0, db_1.saveCommunicationRecord)(r);
|
|
30
|
+
}
|
|
31
|
+
return { jobCaseCount: jobCases.length, communicationCount: records.length };
|
|
32
|
+
}
|
|
33
|
+
function runCacheClear() {
|
|
34
|
+
const db = (0, db_1.getDb)();
|
|
35
|
+
db.exec('DELETE FROM job_cases; DELETE FROM communication_records; DELETE FROM search_history;');
|
|
36
|
+
}
|
|
37
|
+
exports.cacheCommand = new commander_1.Command('cache')
|
|
38
|
+
.description('本地缓存管理');
|
|
39
|
+
exports.cacheCommand
|
|
40
|
+
.command('sync')
|
|
41
|
+
.description('从服务器同步 JobCase 与沟通记录到本地 SQLite')
|
|
42
|
+
.action(async () => {
|
|
43
|
+
try {
|
|
44
|
+
const result = await runCacheSync();
|
|
45
|
+
console.log(`同步完成:JobCase ${result.jobCaseCount} 条,沟通记录 ${result.communicationCount} 条`);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
console.error('同步失败:', err.response?.data?.message || err.message);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
exports.cacheCommand
|
|
53
|
+
.command('clear')
|
|
54
|
+
.description('清空本地缓存')
|
|
55
|
+
.action(() => {
|
|
56
|
+
runCacheClear();
|
|
57
|
+
console.log('本地缓存已清空');
|
|
58
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export interface ConfigOptions {
|
|
3
|
+
serverUrl?: string;
|
|
4
|
+
watchFolder?: string;
|
|
5
|
+
lang?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function runConfig(options: ConfigOptions): {
|
|
8
|
+
serverUrl: string;
|
|
9
|
+
lang: string;
|
|
10
|
+
watchFolder?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const configCommand: Command;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.configCommand = void 0;
|
|
4
|
+
exports.runConfig = runConfig;
|
|
5
|
+
const commander_1 = require("commander");
|
|
6
|
+
const config_1 = require("../config");
|
|
7
|
+
const i18n_1 = require("../i18n");
|
|
8
|
+
function runConfig(options) {
|
|
9
|
+
const updates = {};
|
|
10
|
+
if (options.serverUrl)
|
|
11
|
+
updates.serverUrl = options.serverUrl;
|
|
12
|
+
if (options.watchFolder)
|
|
13
|
+
updates.watchFolder = options.watchFolder;
|
|
14
|
+
if (options.lang)
|
|
15
|
+
updates.lang = options.lang;
|
|
16
|
+
const saved = (0, config_1.saveConfig)(updates);
|
|
17
|
+
return saved;
|
|
18
|
+
}
|
|
19
|
+
exports.configCommand = new commander_1.Command('config')
|
|
20
|
+
.description(i18n_1.i18n.t('config.description'))
|
|
21
|
+
.option('--server-url <url>', 'Server URL')
|
|
22
|
+
.option('--watch-folder <path>', 'Watch folder for job cases')
|
|
23
|
+
.option('--lang <lang>', 'Output language')
|
|
24
|
+
.action((options) => {
|
|
25
|
+
runConfig(options);
|
|
26
|
+
console.log(i18n_1.i18n.t('config.saved'));
|
|
27
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
interface DoctorCheck {
|
|
3
|
+
key: string;
|
|
4
|
+
status: 'ok' | 'warn' | 'fail' | 'skip';
|
|
5
|
+
detail: string;
|
|
6
|
+
}
|
|
7
|
+
/** 对应 ChromePilot doctor: 全链路健康检查 — 带 auto-heal, 无感启动 server/安装扩展 */
|
|
8
|
+
export declare function runDoctor(opts: {
|
|
9
|
+
json?: boolean;
|
|
10
|
+
openExtensionFolder?: boolean;
|
|
11
|
+
autoHeal?: boolean;
|
|
12
|
+
interactive?: boolean;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
summary: 'pass' | 'warn' | 'fail';
|
|
15
|
+
checks: DoctorCheck[];
|
|
16
|
+
hints: string[];
|
|
17
|
+
healed: string[];
|
|
18
|
+
actions: string[];
|
|
19
|
+
}>;
|
|
20
|
+
export declare const doctorCommand: Command;
|
|
21
|
+
export {};
|