exomind 0.15.0 → 0.17.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 +2 -0
- package/dist/cli.js +49 -8
- package/dist/cli.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -17,6 +17,8 @@ exomind login # 粘贴 youhuale.cn/ui/account 的 API Key
|
|
|
17
17
|
exomind me # 验证登录
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
> **短命令**:`emcli` 是同一入口的别名(`exomind` 太长时的省敲方案),`emcli query "..."` 等价于 `exomind query "..."`,help/usage 会跟随实际调用名。两个名字同时可用;文档示例一律写 `exomind`。
|
|
21
|
+
|
|
20
22
|
> 前置:Node.js 18+(`node -v`)。CI 等场景可改用环境变量 `EXOMIND_API_KEY` / `EXOMIND_BASE_URL`,免登录。
|
|
21
23
|
|
|
22
24
|
## 快速开始
|
package/dist/cli.js
CHANGED
|
@@ -3119,10 +3119,11 @@ var {
|
|
|
3119
3119
|
// package.json
|
|
3120
3120
|
var package_default = {
|
|
3121
3121
|
name: "exomind",
|
|
3122
|
-
version: "0.
|
|
3122
|
+
version: "0.17.0",
|
|
3123
3123
|
description: "ExoMind \u8DE8\u5E73\u53F0\u547D\u4EE4\u884C\u5BA2\u6237\u7AEF \u2014 \u901A\u8FC7 REST \u4E0E ExoMind \u77E5\u8BC6\u98DE\u8F6E\u4EA4\u4E92(ingest/query/search/review),\u66FF\u4EE3 Windows \u4E0D\u53EF\u7528\u7684 MCP \u5BA2\u6237\u7AEF\u3002",
|
|
3124
3124
|
bin: {
|
|
3125
|
-
exomind: "dist/cli.js"
|
|
3125
|
+
exomind: "dist/cli.js",
|
|
3126
|
+
emcli: "dist/cli.js"
|
|
3126
3127
|
},
|
|
3127
3128
|
files: [
|
|
3128
3129
|
"dist",
|
|
@@ -3302,6 +3303,12 @@ function saveConfig(cfg) {
|
|
|
3302
3303
|
} catch {
|
|
3303
3304
|
}
|
|
3304
3305
|
}
|
|
3306
|
+
function invalidateKeywordCache() {
|
|
3307
|
+
try {
|
|
3308
|
+
fs.unlinkSync(CACHE_KEYWORDS);
|
|
3309
|
+
} catch {
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3305
3312
|
function resolveConfig(overrides) {
|
|
3306
3313
|
const base = loadConfig();
|
|
3307
3314
|
return {
|
|
@@ -3632,13 +3639,27 @@ async function getEntityDesc(client, name, deadline = Number.POSITIVE_INFINITY)
|
|
|
3632
3639
|
return { name, description: "" };
|
|
3633
3640
|
}
|
|
3634
3641
|
}
|
|
3642
|
+
var SHORT_ASCII = /^[a-z0-9._#+-]{2,4}$/;
|
|
3643
|
+
function escapeRegExp(s) {
|
|
3644
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3645
|
+
}
|
|
3646
|
+
function hitsPrompt(promptLower, candLower) {
|
|
3647
|
+
if (SHORT_ASCII.test(candLower)) {
|
|
3648
|
+
try {
|
|
3649
|
+
return new RegExp(`(?<![a-z0-9])${escapeRegExp(candLower)}(?![a-z0-9])`).test(promptLower);
|
|
3650
|
+
} catch {
|
|
3651
|
+
return promptLower.includes(candLower);
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
return promptLower.includes(candLower);
|
|
3655
|
+
}
|
|
3635
3656
|
function matchEntities(prompt2, candidates) {
|
|
3636
3657
|
const lower = prompt2.toLowerCase();
|
|
3637
3658
|
const hits = /* @__PURE__ */ new Set();
|
|
3638
3659
|
for (const name of candidates) {
|
|
3639
3660
|
const nl = name.toLowerCase().trim();
|
|
3640
3661
|
if (nl.length < 2) continue;
|
|
3641
|
-
if (lower
|
|
3662
|
+
if (hitsPrompt(lower, nl)) hits.add(name);
|
|
3642
3663
|
}
|
|
3643
3664
|
return [...hits].sort((a, b) => b.length - a.length);
|
|
3644
3665
|
}
|
|
@@ -3664,22 +3685,39 @@ ${desc}
|
|
|
3664
3685
|
}
|
|
3665
3686
|
async function buildContext(client, msg, dedup, now, deadline = Number.POSITIVE_INFINITY) {
|
|
3666
3687
|
const index = await getKeywordIndex(client, deadline);
|
|
3667
|
-
if (!index)
|
|
3688
|
+
if (!index) {
|
|
3689
|
+
return '[ExoMind] \u77E5\u8BC6\u98DE\u8F6E\u4E0A\u4E0B\u6587\u672C\u6B21\u672A\u6CE8\u5165(\u5173\u952E\u8BCD\u8868\u62C9\u53D6\u8D85\u65F6\u6216\u5931\u8D25);\u9700\u8981\u53CD\u67E5\u53EF\u8FD0\u884C `exomind search "\u5173\u952E\u8BCD"`\u3002';
|
|
3690
|
+
}
|
|
3668
3691
|
const candidates = [...index.names || [], ...index.aliases || []];
|
|
3669
3692
|
const matched = matchEntities(msg, candidates);
|
|
3670
3693
|
if (!matched.length) return "";
|
|
3671
3694
|
const picked = [];
|
|
3695
|
+
const pickedCanonical = /* @__PURE__ */ new Set();
|
|
3696
|
+
const cooled = [];
|
|
3672
3697
|
for (const name of matched) {
|
|
3673
3698
|
if (picked.length >= 3) break;
|
|
3674
3699
|
if (budgetMs(deadline) <= 200) break;
|
|
3675
3700
|
const canonical = name;
|
|
3676
|
-
if (dedup.injected[canonical] && now - dedup.injected[canonical] < COOLDOWN_MS)
|
|
3701
|
+
if (dedup.injected[canonical] && now - dedup.injected[canonical] < COOLDOWN_MS) {
|
|
3702
|
+
cooled.push(name);
|
|
3703
|
+
continue;
|
|
3704
|
+
}
|
|
3677
3705
|
const desc = await getEntityDesc(client, name, deadline);
|
|
3678
3706
|
if (desc.description || desc.relationships?.length) {
|
|
3679
|
-
picked.push(desc);
|
|
3680
3707
|
dedup.injected[canonical] = now;
|
|
3708
|
+
const norm = (desc.name || name).toLowerCase();
|
|
3709
|
+
if (pickedCanonical.has(norm)) continue;
|
|
3710
|
+
pickedCanonical.add(norm);
|
|
3711
|
+
picked.push(desc);
|
|
3681
3712
|
}
|
|
3682
3713
|
}
|
|
3714
|
+
if (!picked.length && cooled.length) {
|
|
3715
|
+
const remainMin = Math.ceil(
|
|
3716
|
+
(COOLDOWN_MS - (now - Math.max(...cooled.map((n) => dedup.injected[n] ?? 0)))) / 6e4
|
|
3717
|
+
);
|
|
3718
|
+
const sample = cooled.slice(0, 2).map((n) => `"${n}"`).join("\u3001");
|
|
3719
|
+
return `[ExoMind] ${sample} \u7B49\u5173\u952E\u8BCD\u7684\u4E0A\u4E0B\u6587\u5DF2\u6CE8\u5165\u8FC7,30 \u5206\u949F\u53BB\u91CD\u51B7\u5374\u8FD8\u5269 ${remainMin} \u5206\u949F,\u672C\u6B21\u8DF3\u8FC7;\u9700\u8981\u8BE6\u60C5\u53EF\u8FD0\u884C \`exomind entity "${cooled[0]}"\`\u3002`;
|
|
3720
|
+
}
|
|
3683
3721
|
return contextBlock(picked);
|
|
3684
3722
|
}
|
|
3685
3723
|
async function runHook(client) {
|
|
@@ -4070,7 +4108,9 @@ async function ingestWithRetry(client, payload, timeoutMs, url = "/ingest") {
|
|
|
4070
4108
|
let transientAttempts = 0;
|
|
4071
4109
|
while (true) {
|
|
4072
4110
|
try {
|
|
4073
|
-
|
|
4111
|
+
const res = await client.post(url, payload, { timeoutMs });
|
|
4112
|
+
invalidateKeywordCache();
|
|
4113
|
+
return res;
|
|
4074
4114
|
} catch (e) {
|
|
4075
4115
|
if (!(e instanceof ApiError)) throw e;
|
|
4076
4116
|
if (e.status === 429) {
|
|
@@ -5334,7 +5374,8 @@ function run(fn) {
|
|
|
5334
5374
|
};
|
|
5335
5375
|
}
|
|
5336
5376
|
var program2 = new Command();
|
|
5337
|
-
|
|
5377
|
+
var binName = (process.argv[1] ?? "").endsWith("/emcli") ? "emcli" : "exomind";
|
|
5378
|
+
program2.name(binName).description("ExoMind \u8DE8\u5E73\u53F0\u77E5\u8BC6\u98DE\u8F6E\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");
|
|
5338
5379
|
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));
|
|
5339
5380
|
program2.command("me").description("\u663E\u793A\u5F53\u524D\u767B\u5F55\u6001\u4E0E\u670D\u52A1\u5668").action(run(whoami));
|
|
5340
5381
|
program2.command("whoami", { hidden: true }).description("\u663E\u793A\u5F53\u524D\u767B\u5F55\u6001\u4E0E\u670D\u52A1\u5668(me \u7684\u65E7\u540D)").action(run(whoami));
|