exomind 0.1.3 → 0.2.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/README.md +1 -0
- package/dist/cli.js +183 -34
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +16 -1
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ exomind whoami # 验证登录
|
|
|
26
26
|
exomind ingest "Redis 持久化:RDB 快照 + AOF 日志,混合模式推荐" -t "Redis 持久化" --tag redis
|
|
27
27
|
echo "管道内容" | exomind ingest -t "标题"
|
|
28
28
|
exomind ingest --file ./notes.md -t "标题"
|
|
29
|
+
exomind ingest --dir ./notes --recursive # 目录批量(增量: SHA-256 跳过未变文件)
|
|
29
30
|
|
|
30
31
|
# 查询与搜索
|
|
31
32
|
exomind query "Redis RDB 和 AOF 的区别?"
|
package/dist/cli.js
CHANGED
|
@@ -965,8 +965,8 @@ var require_command = __commonJS({
|
|
|
965
965
|
"use strict";
|
|
966
966
|
var EventEmitter = require("events").EventEmitter;
|
|
967
967
|
var childProcess = require("child_process");
|
|
968
|
-
var
|
|
969
|
-
var
|
|
968
|
+
var path6 = require("path");
|
|
969
|
+
var fs7 = require("fs");
|
|
970
970
|
var process2 = require("process");
|
|
971
971
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
972
972
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1898,11 +1898,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1898
1898
|
let launchWithNode = false;
|
|
1899
1899
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1900
1900
|
function findFile(baseDir, baseName) {
|
|
1901
|
-
const localBin =
|
|
1902
|
-
if (
|
|
1903
|
-
if (sourceExt.includes(
|
|
1901
|
+
const localBin = path6.resolve(baseDir, baseName);
|
|
1902
|
+
if (fs7.existsSync(localBin)) return localBin;
|
|
1903
|
+
if (sourceExt.includes(path6.extname(baseName))) return void 0;
|
|
1904
1904
|
const foundExt = sourceExt.find(
|
|
1905
|
-
(ext) =>
|
|
1905
|
+
(ext) => fs7.existsSync(`${localBin}${ext}`)
|
|
1906
1906
|
);
|
|
1907
1907
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1908
1908
|
return void 0;
|
|
@@ -1914,21 +1914,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1914
1914
|
if (this._scriptPath) {
|
|
1915
1915
|
let resolvedScriptPath;
|
|
1916
1916
|
try {
|
|
1917
|
-
resolvedScriptPath =
|
|
1917
|
+
resolvedScriptPath = fs7.realpathSync(this._scriptPath);
|
|
1918
1918
|
} catch (err) {
|
|
1919
1919
|
resolvedScriptPath = this._scriptPath;
|
|
1920
1920
|
}
|
|
1921
|
-
executableDir =
|
|
1922
|
-
|
|
1921
|
+
executableDir = path6.resolve(
|
|
1922
|
+
path6.dirname(resolvedScriptPath),
|
|
1923
1923
|
executableDir
|
|
1924
1924
|
);
|
|
1925
1925
|
}
|
|
1926
1926
|
if (executableDir) {
|
|
1927
1927
|
let localFile = findFile(executableDir, executableFile);
|
|
1928
1928
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1929
|
-
const legacyName =
|
|
1929
|
+
const legacyName = path6.basename(
|
|
1930
1930
|
this._scriptPath,
|
|
1931
|
-
|
|
1931
|
+
path6.extname(this._scriptPath)
|
|
1932
1932
|
);
|
|
1933
1933
|
if (legacyName !== this._name) {
|
|
1934
1934
|
localFile = findFile(
|
|
@@ -1939,7 +1939,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1939
1939
|
}
|
|
1940
1940
|
executableFile = localFile || executableFile;
|
|
1941
1941
|
}
|
|
1942
|
-
launchWithNode = sourceExt.includes(
|
|
1942
|
+
launchWithNode = sourceExt.includes(path6.extname(executableFile));
|
|
1943
1943
|
let proc;
|
|
1944
1944
|
if (process2.platform !== "win32") {
|
|
1945
1945
|
if (launchWithNode) {
|
|
@@ -2779,7 +2779,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2779
2779
|
* @return {Command}
|
|
2780
2780
|
*/
|
|
2781
2781
|
nameFromFilename(filename) {
|
|
2782
|
-
this._name =
|
|
2782
|
+
this._name = path6.basename(filename, path6.extname(filename));
|
|
2783
2783
|
return this;
|
|
2784
2784
|
}
|
|
2785
2785
|
/**
|
|
@@ -2793,9 +2793,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2793
2793
|
* @param {string} [path]
|
|
2794
2794
|
* @return {(string|null|Command)}
|
|
2795
2795
|
*/
|
|
2796
|
-
executableDir(
|
|
2797
|
-
if (
|
|
2798
|
-
this._executableDir =
|
|
2796
|
+
executableDir(path7) {
|
|
2797
|
+
if (path7 === void 0) return this._executableDir;
|
|
2798
|
+
this._executableDir = path7;
|
|
2799
2799
|
return this;
|
|
2800
2800
|
}
|
|
2801
2801
|
/**
|
|
@@ -3119,7 +3119,7 @@ var {
|
|
|
3119
3119
|
// package.json
|
|
3120
3120
|
var package_default = {
|
|
3121
3121
|
name: "exomind",
|
|
3122
|
-
version: "0.
|
|
3122
|
+
version: "0.2.0",
|
|
3123
3123
|
description: "ExoMind \u8DE8\u5E73\u53F0\u547D\u4EE4\u884C\u5BA2\u6237\u7AEF \u2014 \u901A\u8FC7 REST \u4E0E ExoMind \u77E5\u8BC6\u5E93\u4EA4\u4E92(ingest/query/search/review),\u66FF\u4EE3 Windows \u4E0D\u53EF\u7528\u7684 MCP \u5BA2\u6237\u7AEF\u3002",
|
|
3124
3124
|
bin: {
|
|
3125
3125
|
exomind: "dist/cli.js"
|
|
@@ -3624,8 +3624,157 @@ async function runHook(client) {
|
|
|
3624
3624
|
}
|
|
3625
3625
|
}
|
|
3626
3626
|
|
|
3627
|
+
// src/ingest_dir.ts
|
|
3628
|
+
var fs5 = __toESM(require("fs"));
|
|
3629
|
+
var path4 = __toESM(require("path"));
|
|
3630
|
+
|
|
3631
|
+
// src/manifest.ts
|
|
3632
|
+
var fs4 = __toESM(require("fs"));
|
|
3633
|
+
var path3 = __toESM(require("path"));
|
|
3634
|
+
var crypto = __toESM(require("crypto"));
|
|
3635
|
+
var MANIFEST_FILE = path3.join(CONFIG_DIR, "manifest.json");
|
|
3636
|
+
function sha256(s) {
|
|
3637
|
+
return crypto.createHash("sha256").update(s, "utf-8").digest("hex");
|
|
3638
|
+
}
|
|
3639
|
+
function loadManifest() {
|
|
3640
|
+
try {
|
|
3641
|
+
const d = JSON.parse(fs4.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
3642
|
+
return d && typeof d === "object" ? d : {};
|
|
3643
|
+
} catch {
|
|
3644
|
+
return {};
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
function saveManifest(m) {
|
|
3648
|
+
try {
|
|
3649
|
+
fs4.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
3650
|
+
fs4.writeFileSync(MANIFEST_FILE, JSON.stringify(m, null, 2));
|
|
3651
|
+
} catch {
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
function cleanupStale(m, dir, currentFiles) {
|
|
3655
|
+
const prefix = path3.resolve(dir) + path3.sep;
|
|
3656
|
+
const current = new Set(currentFiles);
|
|
3657
|
+
for (const key of Object.keys(m)) {
|
|
3658
|
+
if (key.startsWith(prefix) && !current.has(key)) {
|
|
3659
|
+
delete m[key];
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3664
|
+
// src/ingest_dir.ts
|
|
3665
|
+
function globToRegex(pattern) {
|
|
3666
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
3667
|
+
return new RegExp(`^${escaped}$`);
|
|
3668
|
+
}
|
|
3669
|
+
function walkDir(dir, recursive, pattern) {
|
|
3670
|
+
const rx = globToRegex(pattern);
|
|
3671
|
+
const out = [];
|
|
3672
|
+
const walk = (d) => {
|
|
3673
|
+
let entries;
|
|
3674
|
+
try {
|
|
3675
|
+
entries = fs5.readdirSync(d, { withFileTypes: true });
|
|
3676
|
+
} catch {
|
|
3677
|
+
return;
|
|
3678
|
+
}
|
|
3679
|
+
for (const e of entries) {
|
|
3680
|
+
if (e.name.startsWith(".")) continue;
|
|
3681
|
+
const full = path4.join(d, e.name);
|
|
3682
|
+
if (e.isDirectory()) {
|
|
3683
|
+
if (recursive) walk(full);
|
|
3684
|
+
} else if (e.isFile() && rx.test(e.name)) {
|
|
3685
|
+
out.push(full);
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
};
|
|
3689
|
+
walk(path4.resolve(dir));
|
|
3690
|
+
return out.sort();
|
|
3691
|
+
}
|
|
3692
|
+
function deriveTitle(file, content) {
|
|
3693
|
+
for (const line of content.split("\n")) {
|
|
3694
|
+
const m = line.match(/^#\s+(.+?)\s*$/);
|
|
3695
|
+
if (m) return m[1].trim();
|
|
3696
|
+
}
|
|
3697
|
+
return path4.basename(file).replace(/\.[^.]+$/, "");
|
|
3698
|
+
}
|
|
3699
|
+
function planIngestest(files, manifest, force) {
|
|
3700
|
+
const toIngest = [];
|
|
3701
|
+
const toSkip = [];
|
|
3702
|
+
for (const f of files) {
|
|
3703
|
+
let content;
|
|
3704
|
+
try {
|
|
3705
|
+
content = readFileText(f);
|
|
3706
|
+
} catch {
|
|
3707
|
+
continue;
|
|
3708
|
+
}
|
|
3709
|
+
const hash = sha256(content);
|
|
3710
|
+
const prev = manifest[f];
|
|
3711
|
+
if (!force && prev && prev.hash === hash) {
|
|
3712
|
+
toSkip.push(f);
|
|
3713
|
+
} else {
|
|
3714
|
+
toIngest.push({ path: f, content, title: deriveTitle(f, content), hash });
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
return { toIngest, toSkip };
|
|
3718
|
+
}
|
|
3719
|
+
async function runDirIngestest(client, opts, dir) {
|
|
3720
|
+
const files = walkDir(dir, !!opts.recursive, opts.pattern || "*.md");
|
|
3721
|
+
if (!files.length) {
|
|
3722
|
+
console.log(dim(`\u76EE\u5F55 ${dir} \u4E0B\u65E0\u5339\u914D\u6587\u4EF6\u3002`));
|
|
3723
|
+
return;
|
|
3724
|
+
}
|
|
3725
|
+
const manifest = loadManifest();
|
|
3726
|
+
const plan = planIngestest(files, manifest, !!opts.force);
|
|
3727
|
+
const total = files.length;
|
|
3728
|
+
process.stderr.write(
|
|
3729
|
+
`\u76EE\u5F55 ${dir}: ${total} \u6587\u4EF6 \u2192 \u65B0\u589E/\u66F4\u65B0 ${plan.toIngest.length},\u8DF3\u8FC7 ${plan.toSkip.length}
|
|
3730
|
+
`
|
|
3731
|
+
);
|
|
3732
|
+
let added = 0;
|
|
3733
|
+
let updated = 0;
|
|
3734
|
+
let failed = 0;
|
|
3735
|
+
for (let i = 0; i < plan.toIngest.length; i++) {
|
|
3736
|
+
const f = plan.toIngest[i];
|
|
3737
|
+
const prev = manifest[f.path];
|
|
3738
|
+
process.stderr.write(
|
|
3739
|
+
`\u23F3 [${i + 1}/${plan.toIngest.length}] ${path4.basename(f.path)} ${prev ? "(\u66F4\u65B0)" : "(\u65B0\u589E)"}\u2026
|
|
3740
|
+
`
|
|
3741
|
+
);
|
|
3742
|
+
try {
|
|
3743
|
+
await client.post(
|
|
3744
|
+
"/ingest",
|
|
3745
|
+
{ content: f.content, title: f.title, tags: opts.tag },
|
|
3746
|
+
{ timeoutMs: opTimeout(3e5) }
|
|
3747
|
+
);
|
|
3748
|
+
manifest[f.path] = {
|
|
3749
|
+
hash: f.hash,
|
|
3750
|
+
ingested_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3751
|
+
title: f.title,
|
|
3752
|
+
size: f.content.length
|
|
3753
|
+
};
|
|
3754
|
+
if (prev) updated++;
|
|
3755
|
+
else added++;
|
|
3756
|
+
saveManifest(manifest);
|
|
3757
|
+
} catch (e) {
|
|
3758
|
+
failed++;
|
|
3759
|
+
process.stderr.write(` ${red("\u2717")} ${path4.basename(f.path)}: ${e.message}
|
|
3760
|
+
`);
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
cleanupStale(manifest, dir, files);
|
|
3764
|
+
saveManifest(manifest);
|
|
3765
|
+
output(
|
|
3766
|
+
{ added, updated, skipped: plan.toSkip.length, failed, total, dir },
|
|
3767
|
+
() => console.log(
|
|
3768
|
+
green("\u2713 \u76EE\u5F55\u6444\u5165\u5B8C\u6210") + dim(
|
|
3769
|
+
`: \u65B0\u589E ${added} / \u66F4\u65B0 ${updated} / \u8DF3\u8FC7 ${plan.toSkip.length} / \u5931\u8D25 ${failed} (\u5171 ${total})`
|
|
3770
|
+
)
|
|
3771
|
+
)
|
|
3772
|
+
);
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3627
3775
|
// src/commands/ingest.ts
|
|
3628
3776
|
async function ingest(client, opts, args) {
|
|
3777
|
+
if (opts.dir) return runDirIngestest(client, opts, opts.dir);
|
|
3629
3778
|
let content = "";
|
|
3630
3779
|
if (opts.file) {
|
|
3631
3780
|
content = readFileText(opts.file);
|
|
@@ -3946,40 +4095,40 @@ async function whoami(client) {
|
|
|
3946
4095
|
}
|
|
3947
4096
|
|
|
3948
4097
|
// src/commands/install.ts
|
|
3949
|
-
var
|
|
3950
|
-
var
|
|
4098
|
+
var fs6 = __toESM(require("fs"));
|
|
4099
|
+
var path5 = __toESM(require("path"));
|
|
3951
4100
|
var os2 = __toESM(require("os"));
|
|
3952
|
-
var PKG_ROOT =
|
|
3953
|
-
var SKILL_SRC =
|
|
4101
|
+
var PKG_ROOT = path5.resolve(__dirname, "..");
|
|
4102
|
+
var SKILL_SRC = path5.join(PKG_ROOT, "skill", "SKILL.md");
|
|
3954
4103
|
function readJson2(file) {
|
|
3955
4104
|
try {
|
|
3956
|
-
return JSON.parse(
|
|
4105
|
+
return JSON.parse(fs6.readFileSync(file, "utf-8"));
|
|
3957
4106
|
} catch {
|
|
3958
4107
|
return null;
|
|
3959
4108
|
}
|
|
3960
4109
|
}
|
|
3961
4110
|
function backup(file) {
|
|
3962
|
-
if (!
|
|
4111
|
+
if (!fs6.existsSync(file)) return;
|
|
3963
4112
|
const bak = `${file}.bak-${Date.now()}`;
|
|
3964
4113
|
try {
|
|
3965
|
-
|
|
3966
|
-
console.log(dim(` (\u5DF2\u5907\u4EFD\u539F\u914D\u7F6E \u2192 ${
|
|
4114
|
+
fs6.copyFileSync(file, bak);
|
|
4115
|
+
console.log(dim(` (\u5DF2\u5907\u4EFD\u539F\u914D\u7F6E \u2192 ${path5.basename(bak)})`));
|
|
3967
4116
|
} catch {
|
|
3968
4117
|
}
|
|
3969
4118
|
}
|
|
3970
4119
|
async function install(_client, opts) {
|
|
3971
|
-
const claudeDir =
|
|
3972
|
-
const skillDestDir =
|
|
3973
|
-
if (!
|
|
4120
|
+
const claudeDir = path5.join(os2.homedir(), ".claude");
|
|
4121
|
+
const skillDestDir = path5.join(claudeDir, "skills", "exomind");
|
|
4122
|
+
if (!fs6.existsSync(SKILL_SRC)) {
|
|
3974
4123
|
throw new Error(`skill \u6E90\u4E0D\u5B58\u5728: ${SKILL_SRC}(npm \u5305\u53EF\u80FD\u635F\u574F,\u6216\u5F00\u53D1\u6A21\u5F0F\u4E0B\u672A\u6784\u5EFA)`);
|
|
3975
4124
|
}
|
|
3976
|
-
|
|
3977
|
-
|
|
4125
|
+
fs6.mkdirSync(skillDestDir, { recursive: true });
|
|
4126
|
+
fs6.copyFileSync(SKILL_SRC, path5.join(skillDestDir, "SKILL.md"));
|
|
3978
4127
|
console.log(ok("\u5DF2\u5B89\u88C5 Claude Code skill"));
|
|
3979
4128
|
console.log(dim(` \u2192 ${skillDestDir}/SKILL.md`));
|
|
3980
4129
|
if (opts.withHook) {
|
|
3981
|
-
const settingsFile =
|
|
3982
|
-
|
|
4130
|
+
const settingsFile = path5.join(claudeDir, "settings.json");
|
|
4131
|
+
fs6.mkdirSync(claudeDir, { recursive: true });
|
|
3983
4132
|
backup(settingsFile);
|
|
3984
4133
|
const settings = readJson2(settingsFile) ?? {};
|
|
3985
4134
|
const hooks = settings.hooks ?? {};
|
|
@@ -3992,7 +4141,7 @@ async function install(_client, opts) {
|
|
|
3992
4141
|
});
|
|
3993
4142
|
hooks.UserPromptSubmit = kept;
|
|
3994
4143
|
settings.hooks = hooks;
|
|
3995
|
-
|
|
4144
|
+
fs6.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + "\n");
|
|
3996
4145
|
console.log(ok("\u5DF2\u914D\u7F6E UserPromptSubmit hook \u2192 exomind hook"));
|
|
3997
4146
|
console.log(dim(" \u91CD\u542F Claude Code \u751F\u6548\u3002hook \u51FA\u9519\u4E0D\u4F1A\u963B\u585E\u4F60\u7684\u8F93\u5165(\u9519\u8BEF\u53EA\u8FDB stderr)\u3002"));
|
|
3998
4147
|
}
|
|
@@ -4043,7 +4192,7 @@ var program2 = new Command();
|
|
|
4043
4192
|
program2.name("exomind").description("ExoMind \u8DE8\u5E73\u53F0\u77E5\u8BC6\u5E93\u5BA2\u6237\u7AEF \u2014 \u901A\u8FC7 REST \u4EA4\u4E92(\u66FF\u4EE3 Windows MCP \u5BA2\u6237\u7AEF)\u3002").version(VERSION).option("--json", "\u8F93\u51FA\u539F\u59CB JSON(\u673A\u5668\u53EF\u8BFB)").option("--base-url <url>", "\u8986\u76D6\u670D\u52A1\u5668\u5730\u5740").option("--api-key <key>", "\u8986\u76D6 API Key / \u51ED\u8BC1");
|
|
4044
4193
|
program2.command("login").description("\u914D\u7F6E\u670D\u52A1\u5668\u5730\u5740\u4E0E\u51ED\u8BC1,\u5199\u5165 ~/.exomind/config.json").option("--base-url <url>", "\u670D\u52A1\u5668\u5730\u5740").option("--api-key <key>", "API Key \u6216\u767B\u5F55 token(\u4E0D\u586B\u5219\u4EA4\u4E92\u8F93\u5165)").action(run(login));
|
|
4045
4194
|
program2.command("whoami").description("\u663E\u793A\u5F53\u524D\u767B\u5F55\u6001\u4E0E\u670D\u52A1\u5668").action(run(whoami));
|
|
4046
|
-
program2.command("ingest [content...]").description("\u5BFC\u5165\u77E5\u8BC6: \u53C2\u6570\u6587\u672C / --file / stdin
|
|
4195
|
+
program2.command("ingest [content...]").description("\u5BFC\u5165\u77E5\u8BC6: \u53C2\u6570\u6587\u672C / --file / stdin / --dir \u76EE\u5F55\u6279\u91CF(\u589E\u91CF)").option("-t, --title <title>", "\u6807\u9898(\u5355\u6587\u4EF6\u6A21\u5F0F)").option("--tag <tag>", "\u6807\u7B7E(\u53EF\u91CD\u590D)", collect, []).option("--file <path>", "\u4ECE\u6587\u4EF6\u8BFB\u53D6\u5185\u5BB9").option("--dir <path>", "\u76EE\u5F55\u6279\u91CF\u6444\u5165(\u589E\u91CF: \u5185\u5BB9\u54C8\u5E0C\u8DF3\u8FC7\u672A\u53D8\u6587\u4EF6)").option("-r, --recursive", "\u9012\u5F52\u5B50\u76EE\u5F55(\u914D\u5408 --dir)").option("--pattern <glob>", "\u6587\u4EF6\u540D\u5339\u914D,\u9ED8\u8BA4 *.md", "*.md").option("--force", "\u5FFD\u7565 manifest,\u5F3A\u5236\u5168\u91CF\u91CD\u6444(\u914D\u5408 --dir)").action(run(ingest));
|
|
4047
4196
|
program2.command("query [question...]").description("LLM \u95EE\u7B54").option("--tag <tag>", "\u6807\u7B7E\u8FC7\u6EE4(\u53EF\u91CD\u590D)", collect, []).option("--model <name>", "\u6A21\u578B").action(run(query));
|
|
4048
4197
|
program2.command("search [keyword...]").description("\u5168\u6587/\u6DF7\u5408/\u7CBE\u6392\u641C\u7D22").option("-l, --limit <n>", "\u8FD4\u56DE\u6570\u91CF", "10").option("--rerank", "LLM \u7CBE\u6392(\u66F4\u51C6\u4F46\u66F4\u6162)").option("--hybrid", "\u6DF7\u5408\u641C\u7D22(BM25+\u8BED\u4E49)").action(run(search));
|
|
4049
4198
|
program2.command("entity [name...]").description("\u5B9E\u4F53\u8BE6\u60C5 + \u5173\u7CFB").action(run(entity));
|