exomind 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +198 -110
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
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 path9 = require("path");
|
|
969
|
+
var fs9 = 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 = path9.resolve(baseDir, baseName);
|
|
1902
|
+
if (fs9.existsSync(localBin)) return localBin;
|
|
1903
|
+
if (sourceExt.includes(path9.extname(baseName))) return void 0;
|
|
1904
1904
|
const foundExt = sourceExt.find(
|
|
1905
|
-
(ext) =>
|
|
1905
|
+
(ext) => fs9.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 = fs9.realpathSync(this._scriptPath);
|
|
1918
1918
|
} catch (err) {
|
|
1919
1919
|
resolvedScriptPath = this._scriptPath;
|
|
1920
1920
|
}
|
|
1921
|
-
executableDir =
|
|
1922
|
-
|
|
1921
|
+
executableDir = path9.resolve(
|
|
1922
|
+
path9.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 = path9.basename(
|
|
1930
1930
|
this._scriptPath,
|
|
1931
|
-
|
|
1931
|
+
path9.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(path9.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 = path9.basename(filename, path9.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(path10) {
|
|
2797
|
+
if (path10 === void 0) return this._executableDir;
|
|
2798
|
+
this._executableDir = path10;
|
|
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.10.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"
|
|
@@ -3465,6 +3465,45 @@ function sessionKey() {
|
|
|
3465
3465
|
function dedupPath() {
|
|
3466
3466
|
return path2.join(CACHE_DIR, `dedup-${sessionKey()}.json`);
|
|
3467
3467
|
}
|
|
3468
|
+
var DISCOVER_STATE = path2.join(CACHE_DIR, "discover-state.json");
|
|
3469
|
+
var DISCOVER_FAIL_COOLDOWN_MS = 30 * 60 * 1e3;
|
|
3470
|
+
function buildDiscoverInjection(cards) {
|
|
3471
|
+
if (!cards || !cards.length) return "";
|
|
3472
|
+
const labels = { recap: "\u627E\u56DE", bridge: "\u65B0\u8FDE\u63A5", stub: "\u5F85\u8865\u5168", theme: "\u672C\u5468\u4E3B\u7EBF" };
|
|
3473
|
+
const c = cards[0];
|
|
3474
|
+
return `[ExoMind \u4ECA\u65E5\u53D1\u73B0\xB7${labels[c.type] || c.type}] ${c.reason}
|
|
3475
|
+
\u4ECA\u65E5\u5171 ${cards.length} \u5F20\u5361:\u8FD0\u884C \`exomind entity "${c.name}"\` \u67E5\u770B\u7B2C\u4E00\u6761,\u6216\u6253\u5F00 youhuale.cn/ui/discover \u9010\u5F20\u5904\u7406\u3002\u4E0E\u5F53\u524D\u8BDD\u9898\u65E0\u5173\u5219\u5FFD\u7565\u3002`;
|
|
3476
|
+
}
|
|
3477
|
+
function readDiscoverState() {
|
|
3478
|
+
try {
|
|
3479
|
+
return JSON.parse(fs3.readFileSync(DISCOVER_STATE, "utf-8"));
|
|
3480
|
+
} catch {
|
|
3481
|
+
return {};
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
function saveDiscoverState(st) {
|
|
3485
|
+
try {
|
|
3486
|
+
fs3.mkdirSync(CACHE_DIR, { recursive: true });
|
|
3487
|
+
fs3.writeFileSync(DISCOVER_STATE, JSON.stringify(st));
|
|
3488
|
+
} catch {
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
async function dailyDiscoverInjection(client) {
|
|
3492
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3493
|
+
const st = readDiscoverState();
|
|
3494
|
+
if (st.date === today && st.done) return "";
|
|
3495
|
+
if (st.date === today && st.failed && Date.now() - st.failed < DISCOVER_FAIL_COOLDOWN_MS) {
|
|
3496
|
+
return "";
|
|
3497
|
+
}
|
|
3498
|
+
try {
|
|
3499
|
+
const data = await client.get("/daily-discovery", void 0, { timeoutMs: 8e3 });
|
|
3500
|
+
saveDiscoverState({ date: today, done: true, ts: Date.now() });
|
|
3501
|
+
return buildDiscoverInjection(data.discoveries || []);
|
|
3502
|
+
} catch {
|
|
3503
|
+
saveDiscoverState({ date: today, failed: Date.now() });
|
|
3504
|
+
return "";
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3468
3507
|
function safe(s) {
|
|
3469
3508
|
return s.replace(/[^a-zA-Z0-9一-龥._-]/g, "_").slice(0, 64);
|
|
3470
3509
|
}
|
|
@@ -3634,6 +3673,11 @@ async function runHook(client) {
|
|
|
3634
3673
|
if (ctx) outputs.push(ctx);
|
|
3635
3674
|
} catch {
|
|
3636
3675
|
}
|
|
3676
|
+
try {
|
|
3677
|
+
const discover = await dailyDiscoverInjection(client);
|
|
3678
|
+
if (discover) outputs.push(discover);
|
|
3679
|
+
} catch {
|
|
3680
|
+
}
|
|
3637
3681
|
saveDedup(dedup);
|
|
3638
3682
|
if (outputs.length) {
|
|
3639
3683
|
process.stdout.write(outputs.join("\n\n") + "\n");
|
|
@@ -3794,24 +3838,70 @@ async function runMcpServer(client) {
|
|
|
3794
3838
|
});
|
|
3795
3839
|
}
|
|
3796
3840
|
|
|
3841
|
+
// src/update-check.ts
|
|
3842
|
+
var fs4 = __toESM(require("fs"));
|
|
3843
|
+
var path3 = __toESM(require("path"));
|
|
3844
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3845
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
3846
|
+
function compareVersions(current, latest) {
|
|
3847
|
+
const parse = (v) => v.replace(/^v/, "").split(".").map(Number);
|
|
3848
|
+
const a = parse(current);
|
|
3849
|
+
const b = parse(latest);
|
|
3850
|
+
if (a.length !== 3 || b.length !== 3 || [...a, ...b].some(Number.isNaN)) return 0;
|
|
3851
|
+
for (let i = 0; i < 3; i++) {
|
|
3852
|
+
if (a[i] !== b[i]) return a[i] - b[i];
|
|
3853
|
+
}
|
|
3854
|
+
return 0;
|
|
3855
|
+
}
|
|
3856
|
+
function statePath() {
|
|
3857
|
+
return path3.join(CACHE_DIR, "update-check.json");
|
|
3858
|
+
}
|
|
3859
|
+
async function maybeNotifyUpdate(current, registry = "https://registry.npmjs.org") {
|
|
3860
|
+
if (process.env.EXOMIND_SKIP_UPDATE_CHECK === "1") return;
|
|
3861
|
+
const file = statePath();
|
|
3862
|
+
try {
|
|
3863
|
+
const st = JSON.parse(fs4.readFileSync(file, "utf-8"));
|
|
3864
|
+
if (st.version === current && Date.now() - (st.ts ?? 0) < CHECK_INTERVAL_MS) return;
|
|
3865
|
+
} catch {
|
|
3866
|
+
}
|
|
3867
|
+
let latest = "";
|
|
3868
|
+
try {
|
|
3869
|
+
const ctrl = new AbortController();
|
|
3870
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
|
|
3871
|
+
const r = await fetch(`${registry}/exomind/latest`, { signal: ctrl.signal });
|
|
3872
|
+
clearTimeout(timer);
|
|
3873
|
+
if (r.ok) latest = String((await r.json()).version || "");
|
|
3874
|
+
} catch {
|
|
3875
|
+
}
|
|
3876
|
+
try {
|
|
3877
|
+
fs4.mkdirSync(CACHE_DIR, { recursive: true });
|
|
3878
|
+
fs4.writeFileSync(file, JSON.stringify({ version: current, ts: Date.now(), latest }));
|
|
3879
|
+
} catch {
|
|
3880
|
+
}
|
|
3881
|
+
if (latest && compareVersions(current, latest) < 0) {
|
|
3882
|
+
process.stderr.write(dim(`\u2197 \u53D1\u73B0\u65B0\u7248 v${latest}(\u5F53\u524D v${current}):npm i -g exomind \u5347\u7EA7
|
|
3883
|
+
`));
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
|
|
3797
3887
|
// src/commands/ingest.ts
|
|
3798
|
-
var
|
|
3888
|
+
var path6 = __toESM(require("path"));
|
|
3799
3889
|
|
|
3800
3890
|
// src/ingest_dir.ts
|
|
3801
|
-
var
|
|
3802
|
-
var
|
|
3891
|
+
var fs6 = __toESM(require("fs"));
|
|
3892
|
+
var path5 = __toESM(require("path"));
|
|
3803
3893
|
|
|
3804
3894
|
// src/manifest.ts
|
|
3805
|
-
var
|
|
3806
|
-
var
|
|
3895
|
+
var fs5 = __toESM(require("fs"));
|
|
3896
|
+
var path4 = __toESM(require("path"));
|
|
3807
3897
|
var crypto = __toESM(require("crypto"));
|
|
3808
|
-
var MANIFEST_FILE =
|
|
3898
|
+
var MANIFEST_FILE = path4.join(CONFIG_DIR, "manifest.json");
|
|
3809
3899
|
function sha256(s) {
|
|
3810
3900
|
return crypto.createHash("sha256").update(s, "utf-8").digest("hex");
|
|
3811
3901
|
}
|
|
3812
3902
|
function loadManifest() {
|
|
3813
3903
|
try {
|
|
3814
|
-
const d = JSON.parse(
|
|
3904
|
+
const d = JSON.parse(fs5.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
3815
3905
|
return d && typeof d === "object" ? d : {};
|
|
3816
3906
|
} catch {
|
|
3817
3907
|
return {};
|
|
@@ -3819,8 +3909,8 @@ function loadManifest() {
|
|
|
3819
3909
|
}
|
|
3820
3910
|
function saveManifest(m) {
|
|
3821
3911
|
try {
|
|
3822
|
-
|
|
3823
|
-
|
|
3912
|
+
fs5.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
3913
|
+
fs5.writeFileSync(MANIFEST_FILE, JSON.stringify(m, null, 2));
|
|
3824
3914
|
} catch {
|
|
3825
3915
|
}
|
|
3826
3916
|
}
|
|
@@ -3833,7 +3923,7 @@ function recordFile(manifest, absPath, rawContent, title) {
|
|
|
3833
3923
|
};
|
|
3834
3924
|
}
|
|
3835
3925
|
function cleanupStale(m, dir, currentFiles) {
|
|
3836
|
-
const prefix =
|
|
3926
|
+
const prefix = path4.resolve(dir) + path4.sep;
|
|
3837
3927
|
const current = new Set(currentFiles);
|
|
3838
3928
|
for (const key of Object.keys(m)) {
|
|
3839
3929
|
if (key.startsWith(prefix) && !current.has(key)) {
|
|
@@ -3853,13 +3943,13 @@ function walkDir(dir, recursive, pattern) {
|
|
|
3853
3943
|
const walk = (d) => {
|
|
3854
3944
|
let entries;
|
|
3855
3945
|
try {
|
|
3856
|
-
entries =
|
|
3946
|
+
entries = fs6.readdirSync(d, { withFileTypes: true });
|
|
3857
3947
|
} catch {
|
|
3858
3948
|
return;
|
|
3859
3949
|
}
|
|
3860
3950
|
for (const e of entries) {
|
|
3861
3951
|
if (e.name.startsWith(".")) continue;
|
|
3862
|
-
const full =
|
|
3952
|
+
const full = path5.join(d, e.name);
|
|
3863
3953
|
if (e.isDirectory()) {
|
|
3864
3954
|
if (recursive) walk(full);
|
|
3865
3955
|
} else if (e.isFile() && rx.test(e.name)) {
|
|
@@ -3867,7 +3957,7 @@ function walkDir(dir, recursive, pattern) {
|
|
|
3867
3957
|
}
|
|
3868
3958
|
}
|
|
3869
3959
|
};
|
|
3870
|
-
walk(
|
|
3960
|
+
walk(path5.resolve(dir));
|
|
3871
3961
|
return out.sort();
|
|
3872
3962
|
}
|
|
3873
3963
|
function deriveTitle(file, content) {
|
|
@@ -3875,7 +3965,7 @@ function deriveTitle(file, content) {
|
|
|
3875
3965
|
const m = line.match(/^#\s+(.+?)\s*$/);
|
|
3876
3966
|
if (m) return m[1].trim();
|
|
3877
3967
|
}
|
|
3878
|
-
return
|
|
3968
|
+
return path5.basename(file).replace(/\.[^.]+$/, "");
|
|
3879
3969
|
}
|
|
3880
3970
|
function planIngestest(files, manifest, force) {
|
|
3881
3971
|
const toIngest = [];
|
|
@@ -3965,22 +4055,19 @@ async function ingestWithRetry(client, payload, timeoutMs, url = "/ingest") {
|
|
|
3965
4055
|
}
|
|
3966
4056
|
}
|
|
3967
4057
|
}
|
|
3968
|
-
async function
|
|
3969
|
-
let
|
|
3970
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
4058
|
+
async function retryWith429(fn, maxAttempts = 3) {
|
|
4059
|
+
for (let attempt = 1; ; attempt++) {
|
|
3971
4060
|
try {
|
|
3972
4061
|
return await fn();
|
|
3973
4062
|
} catch (e) {
|
|
3974
|
-
|
|
3975
|
-
const
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
throw e;
|
|
4063
|
+
if (!(e instanceof ApiError) || e.status !== 429 || attempt >= maxAttempts) throw e;
|
|
4064
|
+
const raw = Number(e.headers["retry-after"] ?? e.body?.retry_after ?? 5);
|
|
4065
|
+
const wait = Math.min(Number.isFinite(raw) && raw > 0 ? raw : 5, 60);
|
|
4066
|
+
process.stderr.write(dim(` \u23F8 \u8BF7\u6C42\u9891\u7387\u8D85\u9650,${wait}s \u540E\u91CD\u8BD5 (${attempt}/${maxAttempts})
|
|
4067
|
+
`));
|
|
4068
|
+
await sleep(wait * 1e3);
|
|
3981
4069
|
}
|
|
3982
4070
|
}
|
|
3983
|
-
throw lastErr;
|
|
3984
4071
|
}
|
|
3985
4072
|
async function suspendUntilMidnight(resetEpoch) {
|
|
3986
4073
|
suspended = true;
|
|
@@ -4042,7 +4129,7 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
4042
4129
|
pending.push({ file: f, jobId: res.job_id });
|
|
4043
4130
|
} catch (e) {
|
|
4044
4131
|
failed++;
|
|
4045
|
-
process.stderr.write(` ${red("\u2717")} ${
|
|
4132
|
+
process.stderr.write(` ${red("\u2717")} ${path5.basename(f.path)} \u63D0\u4EA4\u5931\u8D25 \u2014 ${friendlyMsg(e)}
|
|
4046
4133
|
`);
|
|
4047
4134
|
}
|
|
4048
4135
|
});
|
|
@@ -4056,7 +4143,7 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
4056
4143
|
if (Date.now() > pollDeadline) {
|
|
4057
4144
|
for (const [, f] of inFlight) {
|
|
4058
4145
|
failed++;
|
|
4059
|
-
process.stderr.write(` ${red("\u2717")} ${
|
|
4146
|
+
process.stderr.write(` ${red("\u2717")} ${path5.basename(f.path)} \u8F6E\u8BE2\u8D85\u65F6
|
|
4060
4147
|
`);
|
|
4061
4148
|
}
|
|
4062
4149
|
inFlight.clear();
|
|
@@ -4065,13 +4152,13 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
4065
4152
|
await Promise.all(
|
|
4066
4153
|
[...inFlight.entries()].map(async ([jobId, f]) => {
|
|
4067
4154
|
try {
|
|
4068
|
-
const s = await
|
|
4155
|
+
const s = await retryWith429(() => client.get("/ingest/status", { job_id: jobId }));
|
|
4069
4156
|
if (s.status !== "done" && s.status !== "failed") return;
|
|
4070
4157
|
inFlight.delete(jobId);
|
|
4071
4158
|
done++;
|
|
4072
4159
|
if (s.status === "failed") {
|
|
4073
4160
|
failed++;
|
|
4074
|
-
process.stderr.write(` ${red("\u2717")} ${
|
|
4161
|
+
process.stderr.write(` ${red("\u2717")} ${path5.basename(f.path)} \u5904\u7406\u5931\u8D25 \u2014 ${s.error || "\u672A\u77E5\u9519\u8BEF"}
|
|
4075
4162
|
`);
|
|
4076
4163
|
return;
|
|
4077
4164
|
}
|
|
@@ -4080,7 +4167,7 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
4080
4167
|
saveManifest(manifest);
|
|
4081
4168
|
if (s.extracted === false) {
|
|
4082
4169
|
degraded++;
|
|
4083
|
-
process.stderr.write(` \u26A0 ${
|
|
4170
|
+
process.stderr.write(` \u26A0 ${path5.basename(f.path)} \u964D\u7EA7 \u2014 \u539F\u6587\u5DF2\u5B58,\u5B9E\u4F53\u62BD\u53D6\u5F85\u8865\u8DD1
|
|
4084
4171
|
`);
|
|
4085
4172
|
} else if (prev) {
|
|
4086
4173
|
updated++;
|
|
@@ -4136,7 +4223,7 @@ async function ingest(client, opts, args) {
|
|
|
4136
4223
|
if (opts.file) {
|
|
4137
4224
|
fileRaw = readFileText(opts.file);
|
|
4138
4225
|
content = fileRaw;
|
|
4139
|
-
fileAbs =
|
|
4226
|
+
fileAbs = path6.resolve(opts.file);
|
|
4140
4227
|
} else if (args.length && args[0] === "-") {
|
|
4141
4228
|
content = await readStdinForced();
|
|
4142
4229
|
} else if (args.length) {
|
|
@@ -4158,7 +4245,7 @@ async function ingest(client, opts, args) {
|
|
|
4158
4245
|
const result = await ingestWithRetry(client, body, opTimeout(3e5));
|
|
4159
4246
|
if (fileAbs && fileRaw !== null) {
|
|
4160
4247
|
const man = loadManifest();
|
|
4161
|
-
recordFile(man, fileAbs, fileRaw, opts.title ||
|
|
4248
|
+
recordFile(man, fileAbs, fileRaw, opts.title || path6.basename(fileAbs));
|
|
4162
4249
|
saveManifest(man);
|
|
4163
4250
|
}
|
|
4164
4251
|
output(result, () => {
|
|
@@ -4183,7 +4270,7 @@ async function query(client, opts, args) {
|
|
|
4183
4270
|
if (opts.tag?.length) body.tags = opts.tag;
|
|
4184
4271
|
if (opts.model) body.model = opts.model;
|
|
4185
4272
|
hint("\u23F3 \u67E5\u8BE2\u4E2D: LLM \u68C0\u7D22 + \u751F\u6210,\u53EF\u80FD 1-2 \u5206\u949F\u2026");
|
|
4186
|
-
const result = await client.post("/query", body, { timeoutMs: opTimeout(18e4) });
|
|
4273
|
+
const result = await retryWith429(() => client.post("/query", body, { timeoutMs: opTimeout(18e4) }));
|
|
4187
4274
|
output(result, () => {
|
|
4188
4275
|
console.log(result.answer || dim("(\u65E0\u56DE\u7B54)"));
|
|
4189
4276
|
if (result.pages?.length) {
|
|
@@ -4198,12 +4285,12 @@ async function query(client, opts, args) {
|
|
|
4198
4285
|
async function search(client, opts, args) {
|
|
4199
4286
|
const keyword = args.join(" ").trim();
|
|
4200
4287
|
if (!keyword) throw new Error('\u8BF7\u63D0\u4F9B\u5173\u952E\u8BCD: exomind search "\u5173\u952E\u8BCD"');
|
|
4201
|
-
const result = await client.get("/search", {
|
|
4288
|
+
const result = await retryWith429(() => client.get("/search", {
|
|
4202
4289
|
q: keyword,
|
|
4203
4290
|
limit: opts.limit ?? 10,
|
|
4204
4291
|
rerank: opts.rerank ?? false,
|
|
4205
4292
|
hybrid: opts.hybrid ?? false
|
|
4206
|
-
});
|
|
4293
|
+
}));
|
|
4207
4294
|
const results = result.results || [];
|
|
4208
4295
|
output(result, () => {
|
|
4209
4296
|
if (!results.length) {
|
|
@@ -4594,26 +4681,26 @@ async function whoami(client) {
|
|
|
4594
4681
|
}
|
|
4595
4682
|
|
|
4596
4683
|
// src/commands/install.ts
|
|
4597
|
-
var
|
|
4598
|
-
var
|
|
4684
|
+
var fs7 = __toESM(require("fs"));
|
|
4685
|
+
var path7 = __toESM(require("path"));
|
|
4599
4686
|
var os2 = __toESM(require("os"));
|
|
4600
4687
|
var import_node_child_process = require("child_process");
|
|
4601
|
-
var PKG_ROOT =
|
|
4602
|
-
var CLAUDE_SKILL_SRC =
|
|
4603
|
-
var CODEX_SKILL_SRC =
|
|
4688
|
+
var PKG_ROOT = path7.resolve(__dirname, "..");
|
|
4689
|
+
var CLAUDE_SKILL_SRC = path7.join(PKG_ROOT, "skill", "claude", "SKILL.md");
|
|
4690
|
+
var CODEX_SKILL_SRC = path7.join(PKG_ROOT, "skill", "codex", "SKILL.md");
|
|
4604
4691
|
function readJson2(file) {
|
|
4605
4692
|
try {
|
|
4606
|
-
return JSON.parse(
|
|
4693
|
+
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
4607
4694
|
} catch {
|
|
4608
4695
|
return null;
|
|
4609
4696
|
}
|
|
4610
4697
|
}
|
|
4611
4698
|
function backup(file) {
|
|
4612
|
-
if (!
|
|
4699
|
+
if (!fs7.existsSync(file)) return;
|
|
4613
4700
|
const bak = `${file}.bak-${Date.now()}`;
|
|
4614
4701
|
try {
|
|
4615
|
-
|
|
4616
|
-
console.log(dim(` (\u5DF2\u5907\u4EFD \u2192 ${
|
|
4702
|
+
fs7.copyFileSync(file, bak);
|
|
4703
|
+
console.log(dim(` (\u5DF2\u5907\u4EFD \u2192 ${path7.basename(bak)})`));
|
|
4617
4704
|
} catch {
|
|
4618
4705
|
return;
|
|
4619
4706
|
}
|
|
@@ -4621,12 +4708,12 @@ function backup(file) {
|
|
|
4621
4708
|
}
|
|
4622
4709
|
function pruneBackups(file, keep) {
|
|
4623
4710
|
try {
|
|
4624
|
-
const dir =
|
|
4625
|
-
const prefix =
|
|
4626
|
-
const olds =
|
|
4711
|
+
const dir = path7.dirname(file);
|
|
4712
|
+
const prefix = path7.basename(file) + ".bak-";
|
|
4713
|
+
const olds = fs7.readdirSync(dir).filter((f) => f.startsWith(prefix)).map((f) => ({ f, mt: fs7.statSync(path7.join(dir, f)).mtimeMs })).sort((a, b) => b.mt - a.mt);
|
|
4627
4714
|
for (const o of olds.slice(keep)) {
|
|
4628
4715
|
try {
|
|
4629
|
-
|
|
4716
|
+
fs7.unlinkSync(path7.join(dir, o.f));
|
|
4630
4717
|
} catch {
|
|
4631
4718
|
}
|
|
4632
4719
|
}
|
|
@@ -4635,7 +4722,7 @@ function pruneBackups(file, keep) {
|
|
|
4635
4722
|
}
|
|
4636
4723
|
function resolveCodexHome(env = process.env, homeDir = os2.homedir()) {
|
|
4637
4724
|
const configured = (env.CODEX_HOME ?? "").trim();
|
|
4638
|
-
return configured ?
|
|
4725
|
+
return configured ? path7.resolve(configured) : path7.join(homeDir, ".codex");
|
|
4639
4726
|
}
|
|
4640
4727
|
function looksLikeValidToml(text) {
|
|
4641
4728
|
if (!text.trim()) return true;
|
|
@@ -4649,14 +4736,14 @@ function looksLikeValidToml(text) {
|
|
|
4649
4736
|
return brackets === 0;
|
|
4650
4737
|
}
|
|
4651
4738
|
function configureCodexMcp(codexHome) {
|
|
4652
|
-
const codexConfig =
|
|
4739
|
+
const codexConfig = path7.join(codexHome, "config.toml");
|
|
4653
4740
|
try {
|
|
4654
|
-
|
|
4741
|
+
fs7.mkdirSync(path7.dirname(codexConfig), { recursive: true });
|
|
4655
4742
|
} catch {
|
|
4656
4743
|
}
|
|
4657
4744
|
let existing = "";
|
|
4658
4745
|
try {
|
|
4659
|
-
existing =
|
|
4746
|
+
existing = fs7.readFileSync(codexConfig, "utf-8");
|
|
4660
4747
|
} catch {
|
|
4661
4748
|
existing = "";
|
|
4662
4749
|
}
|
|
@@ -4677,24 +4764,24 @@ function configureCodexMcp(codexHome) {
|
|
|
4677
4764
|
command = "exomind"
|
|
4678
4765
|
args = ["mcp"]
|
|
4679
4766
|
`;
|
|
4680
|
-
|
|
4681
|
-
const after =
|
|
4767
|
+
fs7.writeFileSync(codexConfig, existing + snippet);
|
|
4768
|
+
const after = fs7.readFileSync(codexConfig, "utf-8");
|
|
4682
4769
|
if (!/^\[mcp_servers\.exomind\]\s*$/m.test(after) || !looksLikeValidToml(after)) {
|
|
4683
4770
|
return { ok: false, written: true, file: codexConfig, reason: "\u5199\u540E\u590D\u9A8C\u5931\u8D25(\u6BB5\u672A\u5199\u5165\u6216\u6587\u4EF6\u635F\u574F)" };
|
|
4684
4771
|
}
|
|
4685
4772
|
return { ok: true, written: true, file: codexConfig };
|
|
4686
4773
|
}
|
|
4687
4774
|
function installCodexSkill(codexHome, skillSrc = CODEX_SKILL_SRC) {
|
|
4688
|
-
const destDir =
|
|
4689
|
-
const dest =
|
|
4690
|
-
if (!
|
|
4775
|
+
const destDir = path7.join(codexHome, "skills", "exomind");
|
|
4776
|
+
const dest = path7.join(destDir, "SKILL.md");
|
|
4777
|
+
if (!fs7.existsSync(skillSrc)) {
|
|
4691
4778
|
return { ok: false, dest, reason: `skill \u6E90\u4E0D\u5B58\u5728: ${skillSrc}` };
|
|
4692
4779
|
}
|
|
4693
|
-
|
|
4780
|
+
fs7.mkdirSync(destDir, { recursive: true });
|
|
4694
4781
|
backup(dest);
|
|
4695
4782
|
const tmp = `${dest}.tmp-${Date.now()}`;
|
|
4696
|
-
|
|
4697
|
-
|
|
4783
|
+
fs7.writeFileSync(tmp, fs7.readFileSync(skillSrc));
|
|
4784
|
+
fs7.renameSync(tmp, dest);
|
|
4698
4785
|
return { ok: true, dest };
|
|
4699
4786
|
}
|
|
4700
4787
|
function purgeMcpExomind(file) {
|
|
@@ -4705,7 +4792,7 @@ function purgeMcpExomind(file) {
|
|
|
4705
4792
|
delete grp.exomind;
|
|
4706
4793
|
if (Object.keys(grp).length === 0) delete d.mcpServers;
|
|
4707
4794
|
backup(file);
|
|
4708
|
-
|
|
4795
|
+
fs7.writeFileSync(file, JSON.stringify(d, null, 2) + "\n");
|
|
4709
4796
|
return true;
|
|
4710
4797
|
}
|
|
4711
4798
|
function checkMcp(timeoutMs = 6e3) {
|
|
@@ -4757,17 +4844,17 @@ async function install(client, opts) {
|
|
|
4757
4844
|
if (opts.host && !["claude", "codex", "opencode"].includes(opts.host)) {
|
|
4758
4845
|
throw new Error(`\u672A\u77E5 --host: ${opts.host}(\u53EF\u9009: claude | codex | opencode)`);
|
|
4759
4846
|
}
|
|
4760
|
-
const claudeDir =
|
|
4761
|
-
const skillDestDir =
|
|
4847
|
+
const claudeDir = path7.join(os2.homedir(), ".claude");
|
|
4848
|
+
const skillDestDir = path7.join(claudeDir, "skills", "exomind");
|
|
4762
4849
|
const codexHome = resolveCodexHome();
|
|
4763
4850
|
const want = (h) => !opts.host || opts.host === h;
|
|
4764
4851
|
if (opts.skill !== false) {
|
|
4765
4852
|
if (want("claude")) {
|
|
4766
|
-
if (!
|
|
4853
|
+
if (!fs7.existsSync(CLAUDE_SKILL_SRC)) {
|
|
4767
4854
|
throw new Error(`Claude skill \u6E90\u4E0D\u5B58\u5728: ${CLAUDE_SKILL_SRC}(npm \u5305\u53EF\u80FD\u635F\u574F,\u6216\u5F00\u53D1\u6A21\u5F0F\u4E0B\u672A\u6784\u5EFA)`);
|
|
4768
4855
|
}
|
|
4769
|
-
|
|
4770
|
-
|
|
4856
|
+
fs7.mkdirSync(skillDestDir, { recursive: true });
|
|
4857
|
+
fs7.copyFileSync(CLAUDE_SKILL_SRC, path7.join(skillDestDir, "SKILL.md"));
|
|
4771
4858
|
console.log(ok("\u5DF2\u5B89\u88C5 Claude Code skill"));
|
|
4772
4859
|
console.log(dim(` \u2192 ${skillDestDir}/SKILL.md`));
|
|
4773
4860
|
}
|
|
@@ -4782,8 +4869,8 @@ async function install(client, opts) {
|
|
|
4782
4869
|
}
|
|
4783
4870
|
}
|
|
4784
4871
|
if (opts.hook !== false && want("claude")) {
|
|
4785
|
-
const settingsFile =
|
|
4786
|
-
|
|
4872
|
+
const settingsFile = path7.join(claudeDir, "settings.json");
|
|
4873
|
+
fs7.mkdirSync(claudeDir, { recursive: true });
|
|
4787
4874
|
backup(settingsFile);
|
|
4788
4875
|
const settings = readJson2(settingsFile) ?? {};
|
|
4789
4876
|
const hooks = settings.hooks ?? {};
|
|
@@ -4796,31 +4883,31 @@ async function install(client, opts) {
|
|
|
4796
4883
|
});
|
|
4797
4884
|
hooks.UserPromptSubmit = kept;
|
|
4798
4885
|
settings.hooks = hooks;
|
|
4799
|
-
|
|
4886
|
+
fs7.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + "\n");
|
|
4800
4887
|
console.log(ok("\u5DF2\u914D\u7F6E UserPromptSubmit hook \u2192 exomind hook (Claude Code \u72EC\u6709)"));
|
|
4801
4888
|
console.log(dim(" Codex \u5F53\u524D\u65E0\u5BF9\u5E94 prompt-submit hook,\u9760 skill \u89E6\u53D1(jdit/\u5B58\u6863/\u67E5\u8BE2)\u3002"));
|
|
4802
4889
|
}
|
|
4803
4890
|
if (opts.mcp !== false) {
|
|
4804
4891
|
const configured = [];
|
|
4805
4892
|
if (want("claude")) {
|
|
4806
|
-
const ccJson =
|
|
4893
|
+
const ccJson = path7.join(os2.homedir(), ".claude.json");
|
|
4807
4894
|
backup(ccJson);
|
|
4808
4895
|
const cc = readJson2(ccJson) ?? {};
|
|
4809
4896
|
const ccMcp = cc.mcpServers ?? {};
|
|
4810
4897
|
ccMcp.exomind = { command: "exomind", args: ["mcp"] };
|
|
4811
4898
|
cc.mcpServers = ccMcp;
|
|
4812
|
-
|
|
4899
|
+
fs7.writeFileSync(ccJson, JSON.stringify(cc, null, 2) + "\n");
|
|
4813
4900
|
configured.push("Claude Code (~/.claude.json)");
|
|
4814
4901
|
}
|
|
4815
4902
|
if (want("opencode")) {
|
|
4816
|
-
const ocJson =
|
|
4817
|
-
|
|
4903
|
+
const ocJson = path7.join(os2.homedir(), ".config", "opencode", "opencode.json");
|
|
4904
|
+
fs7.mkdirSync(path7.dirname(ocJson), { recursive: true });
|
|
4818
4905
|
backup(ocJson);
|
|
4819
4906
|
const oc = readJson2(ocJson) ?? {};
|
|
4820
4907
|
const ocMcp = oc.mcp ?? {};
|
|
4821
4908
|
ocMcp.exomind = { type: "local", command: ["exomind", "mcp"] };
|
|
4822
4909
|
oc.mcp = ocMcp;
|
|
4823
|
-
|
|
4910
|
+
fs7.writeFileSync(ocJson, JSON.stringify(oc, null, 2) + "\n");
|
|
4824
4911
|
configured.push("OpenCode (~/.config/opencode/opencode.json)");
|
|
4825
4912
|
}
|
|
4826
4913
|
if (want("codex")) {
|
|
@@ -4829,14 +4916,14 @@ async function install(client, opts) {
|
|
|
4829
4916
|
else console.log(yellow(`\u2717 Codex MCP \u914D\u7F6E\u5931\u8D25: ${r.reason}`));
|
|
4830
4917
|
}
|
|
4831
4918
|
const purged = [];
|
|
4832
|
-
if (want("claude") && purgeMcpExomind(
|
|
4919
|
+
if (want("claude") && purgeMcpExomind(path7.join(claudeDir, "settings.json"))) {
|
|
4833
4920
|
purged.push("~/.claude/settings.json");
|
|
4834
4921
|
}
|
|
4835
4922
|
let curDir = process.cwd();
|
|
4836
|
-
for (let i = 0; i < 12 && curDir !==
|
|
4837
|
-
const f =
|
|
4838
|
-
if (
|
|
4839
|
-
curDir =
|
|
4923
|
+
for (let i = 0; i < 12 && curDir !== path7.dirname(curDir); i++) {
|
|
4924
|
+
const f = path7.join(curDir, ".mcp.json");
|
|
4925
|
+
if (fs7.existsSync(f) && purgeMcpExomind(f)) purged.push(f.replace(os2.homedir(), "~"));
|
|
4926
|
+
curDir = path7.dirname(curDir);
|
|
4840
4927
|
}
|
|
4841
4928
|
if (purged.length) console.log(dim(` \u2192 \u6E05\u7406\u6B8B\u7559 exomind MCP \u6761\u76EE(\u907F\u514D\u8986\u76D6 stdio): ${purged.join(", ")}`));
|
|
4842
4929
|
if (configured.length) {
|
|
@@ -4845,12 +4932,12 @@ async function install(client, opts) {
|
|
|
4845
4932
|
console.log(dim(" \u91CD\u542F\u5BF9\u5E94 Agent \u2192 \u62FF\u5230 mcp__exomind__* \u5DE5\u5177"));
|
|
4846
4933
|
}
|
|
4847
4934
|
}
|
|
4848
|
-
const cfgFile =
|
|
4935
|
+
const cfgFile = path7.join(os2.homedir(), ".exomind", "config.json");
|
|
4849
4936
|
const cfg = readJson2(cfgFile);
|
|
4850
4937
|
if (cfg && typeof cfg.base_url === "string" && cfg.base_url.includes("d.youhuale.cn")) {
|
|
4851
4938
|
backup(cfgFile);
|
|
4852
4939
|
cfg.base_url = DEFAULT_BASE_URL;
|
|
4853
|
-
|
|
4940
|
+
fs7.writeFileSync(cfgFile, JSON.stringify(cfg, null, 2) + "\n");
|
|
4854
4941
|
console.log(ok(`\u5DF2\u8FC1\u79FB base_url: d.youhuale.cn \u2192 ${DEFAULT_BASE_URL}`));
|
|
4855
4942
|
console.log(dim(" (stdio MCP \u5B50\u8FDB\u7A0B\u8BFB\u6B64\u914D\u7F6E,\u91CD\u542F Agent \u540E\u7528\u65B0\u57DF\u540D)"));
|
|
4856
4943
|
}
|
|
@@ -4869,10 +4956,10 @@ async function install(client, opts) {
|
|
|
4869
4956
|
}
|
|
4870
4957
|
}
|
|
4871
4958
|
const mcp = live.api_key ? await checkMcp() : { ok: false, detail: "\u672A\u914D\u7F6E API Key" };
|
|
4872
|
-
const codexSkillOk =
|
|
4959
|
+
const codexSkillOk = fs7.existsSync(path7.join(codexHome, "skills", "exomind", "SKILL.md"));
|
|
4873
4960
|
const codexMcfgOk = (() => {
|
|
4874
4961
|
try {
|
|
4875
|
-
return /^\[mcp_servers\.exomind\]\s*$/m.test(
|
|
4962
|
+
return /^\[mcp_servers\.exomind\]\s*$/m.test(fs7.readFileSync(path7.join(codexHome, "config.toml"), "utf-8"));
|
|
4876
4963
|
} catch {
|
|
4877
4964
|
return false;
|
|
4878
4965
|
}
|
|
@@ -4903,19 +4990,19 @@ async function install(client, opts) {
|
|
|
4903
4990
|
}
|
|
4904
4991
|
|
|
4905
4992
|
// src/commands/doctor.ts
|
|
4906
|
-
var
|
|
4907
|
-
var
|
|
4993
|
+
var fs8 = __toESM(require("fs"));
|
|
4994
|
+
var path8 = __toESM(require("path"));
|
|
4908
4995
|
var os3 = __toESM(require("os"));
|
|
4909
4996
|
function readText(p) {
|
|
4910
4997
|
try {
|
|
4911
|
-
return
|
|
4998
|
+
return fs8.readFileSync(p, "utf-8");
|
|
4912
4999
|
} catch {
|
|
4913
5000
|
return "";
|
|
4914
5001
|
}
|
|
4915
5002
|
}
|
|
4916
5003
|
function fileExists(p) {
|
|
4917
5004
|
try {
|
|
4918
|
-
return
|
|
5005
|
+
return fs8.existsSync(p);
|
|
4919
5006
|
} catch {
|
|
4920
5007
|
return false;
|
|
4921
5008
|
}
|
|
@@ -4938,9 +5025,9 @@ function badge(s) {
|
|
|
4938
5025
|
async function doctor(client) {
|
|
4939
5026
|
const codexHome = resolveCodexHome();
|
|
4940
5027
|
const checks = [];
|
|
4941
|
-
const claudeSkill =
|
|
4942
|
-
const claudeSettings =
|
|
4943
|
-
const claudeJson =
|
|
5028
|
+
const claudeSkill = path8.join(os3.homedir(), ".claude", "skills", "exomind", "SKILL.md");
|
|
5029
|
+
const claudeSettings = path8.join(os3.homedir(), ".claude", "settings.json");
|
|
5030
|
+
const claudeJson = path8.join(os3.homedir(), ".claude.json");
|
|
4944
5031
|
checks.push({
|
|
4945
5032
|
host: "claude",
|
|
4946
5033
|
skill: fileExists(claudeSkill) ? "ok" : "missing",
|
|
@@ -4948,8 +5035,8 @@ async function doctor(client) {
|
|
|
4948
5035
|
mcpConfig: mcpServersExomind(claudeJson, "mcpServers") ? "ok" : "missing",
|
|
4949
5036
|
paths: [claudeSkill, claudeSettings, claudeJson]
|
|
4950
5037
|
});
|
|
4951
|
-
const codexSkill =
|
|
4952
|
-
const codexConfig =
|
|
5038
|
+
const codexSkill = path8.join(codexHome, "skills", "exomind", "SKILL.md");
|
|
5039
|
+
const codexConfig = path8.join(codexHome, "config.toml");
|
|
4953
5040
|
checks.push({
|
|
4954
5041
|
host: "codex",
|
|
4955
5042
|
skill: fileExists(codexSkill) ? "ok" : "missing",
|
|
@@ -4958,7 +5045,7 @@ async function doctor(client) {
|
|
|
4958
5045
|
paths: [codexSkill, codexConfig],
|
|
4959
5046
|
detail: "Codex \u65E0 prompt-submit hook,\u9760 skill \u89E6\u53D1(jdit/\u5B58\u6863/\u67E5\u8BE2)"
|
|
4960
5047
|
});
|
|
4961
|
-
const ocJson =
|
|
5048
|
+
const ocJson = path8.join(os3.homedir(), ".config", "opencode", "opencode.json");
|
|
4962
5049
|
checks.push({
|
|
4963
5050
|
host: "opencode",
|
|
4964
5051
|
skill: "unsupported",
|
|
@@ -5036,6 +5123,7 @@ function run(fn) {
|
|
|
5036
5123
|
const client = new ApiClient(cfg);
|
|
5037
5124
|
try {
|
|
5038
5125
|
await fn(client, command.opts(), command.args);
|
|
5126
|
+
if (!root.json) await maybeNotifyUpdate(VERSION);
|
|
5039
5127
|
} catch (e) {
|
|
5040
5128
|
handleError(e);
|
|
5041
5129
|
}
|