exomind 0.9.0 → 0.11.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 +165 -113
- 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.11.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"
|
|
@@ -3838,24 +3838,70 @@ async function runMcpServer(client) {
|
|
|
3838
3838
|
});
|
|
3839
3839
|
}
|
|
3840
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
|
+
|
|
3841
3887
|
// src/commands/ingest.ts
|
|
3842
|
-
var
|
|
3888
|
+
var path6 = __toESM(require("path"));
|
|
3843
3889
|
|
|
3844
3890
|
// src/ingest_dir.ts
|
|
3845
|
-
var
|
|
3846
|
-
var
|
|
3891
|
+
var fs6 = __toESM(require("fs"));
|
|
3892
|
+
var path5 = __toESM(require("path"));
|
|
3847
3893
|
|
|
3848
3894
|
// src/manifest.ts
|
|
3849
|
-
var
|
|
3850
|
-
var
|
|
3895
|
+
var fs5 = __toESM(require("fs"));
|
|
3896
|
+
var path4 = __toESM(require("path"));
|
|
3851
3897
|
var crypto = __toESM(require("crypto"));
|
|
3852
|
-
var MANIFEST_FILE =
|
|
3898
|
+
var MANIFEST_FILE = path4.join(CONFIG_DIR, "manifest.json");
|
|
3853
3899
|
function sha256(s) {
|
|
3854
3900
|
return crypto.createHash("sha256").update(s, "utf-8").digest("hex");
|
|
3855
3901
|
}
|
|
3856
3902
|
function loadManifest() {
|
|
3857
3903
|
try {
|
|
3858
|
-
const d = JSON.parse(
|
|
3904
|
+
const d = JSON.parse(fs5.readFileSync(MANIFEST_FILE, "utf-8"));
|
|
3859
3905
|
return d && typeof d === "object" ? d : {};
|
|
3860
3906
|
} catch {
|
|
3861
3907
|
return {};
|
|
@@ -3863,8 +3909,8 @@ function loadManifest() {
|
|
|
3863
3909
|
}
|
|
3864
3910
|
function saveManifest(m) {
|
|
3865
3911
|
try {
|
|
3866
|
-
|
|
3867
|
-
|
|
3912
|
+
fs5.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
3913
|
+
fs5.writeFileSync(MANIFEST_FILE, JSON.stringify(m, null, 2));
|
|
3868
3914
|
} catch {
|
|
3869
3915
|
}
|
|
3870
3916
|
}
|
|
@@ -3877,7 +3923,7 @@ function recordFile(manifest, absPath, rawContent, title) {
|
|
|
3877
3923
|
};
|
|
3878
3924
|
}
|
|
3879
3925
|
function cleanupStale(m, dir, currentFiles) {
|
|
3880
|
-
const prefix =
|
|
3926
|
+
const prefix = path4.resolve(dir) + path4.sep;
|
|
3881
3927
|
const current = new Set(currentFiles);
|
|
3882
3928
|
for (const key of Object.keys(m)) {
|
|
3883
3929
|
if (key.startsWith(prefix) && !current.has(key)) {
|
|
@@ -3897,13 +3943,13 @@ function walkDir(dir, recursive, pattern) {
|
|
|
3897
3943
|
const walk = (d) => {
|
|
3898
3944
|
let entries;
|
|
3899
3945
|
try {
|
|
3900
|
-
entries =
|
|
3946
|
+
entries = fs6.readdirSync(d, { withFileTypes: true });
|
|
3901
3947
|
} catch {
|
|
3902
3948
|
return;
|
|
3903
3949
|
}
|
|
3904
3950
|
for (const e of entries) {
|
|
3905
3951
|
if (e.name.startsWith(".")) continue;
|
|
3906
|
-
const full =
|
|
3952
|
+
const full = path5.join(d, e.name);
|
|
3907
3953
|
if (e.isDirectory()) {
|
|
3908
3954
|
if (recursive) walk(full);
|
|
3909
3955
|
} else if (e.isFile() && rx.test(e.name)) {
|
|
@@ -3911,7 +3957,7 @@ function walkDir(dir, recursive, pattern) {
|
|
|
3911
3957
|
}
|
|
3912
3958
|
}
|
|
3913
3959
|
};
|
|
3914
|
-
walk(
|
|
3960
|
+
walk(path5.resolve(dir));
|
|
3915
3961
|
return out.sort();
|
|
3916
3962
|
}
|
|
3917
3963
|
function deriveTitle(file, content) {
|
|
@@ -3919,7 +3965,7 @@ function deriveTitle(file, content) {
|
|
|
3919
3965
|
const m = line.match(/^#\s+(.+?)\s*$/);
|
|
3920
3966
|
if (m) return m[1].trim();
|
|
3921
3967
|
}
|
|
3922
|
-
return
|
|
3968
|
+
return path5.basename(file).replace(/\.[^.]+$/, "");
|
|
3923
3969
|
}
|
|
3924
3970
|
function planIngestest(files, manifest, force) {
|
|
3925
3971
|
const toIngest = [];
|
|
@@ -4009,22 +4055,19 @@ async function ingestWithRetry(client, payload, timeoutMs, url = "/ingest") {
|
|
|
4009
4055
|
}
|
|
4010
4056
|
}
|
|
4011
4057
|
}
|
|
4012
|
-
async function
|
|
4013
|
-
let
|
|
4014
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
4058
|
+
async function retryWith429(fn, maxAttempts = 3) {
|
|
4059
|
+
for (let attempt = 1; ; attempt++) {
|
|
4015
4060
|
try {
|
|
4016
4061
|
return await fn();
|
|
4017
4062
|
} catch (e) {
|
|
4018
|
-
|
|
4019
|
-
const
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
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);
|
|
4025
4069
|
}
|
|
4026
4070
|
}
|
|
4027
|
-
throw lastErr;
|
|
4028
4071
|
}
|
|
4029
4072
|
async function suspendUntilMidnight(resetEpoch) {
|
|
4030
4073
|
suspended = true;
|
|
@@ -4086,7 +4129,7 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
4086
4129
|
pending.push({ file: f, jobId: res.job_id });
|
|
4087
4130
|
} catch (e) {
|
|
4088
4131
|
failed++;
|
|
4089
|
-
process.stderr.write(` ${red("\u2717")} ${
|
|
4132
|
+
process.stderr.write(` ${red("\u2717")} ${path5.basename(f.path)} \u63D0\u4EA4\u5931\u8D25 \u2014 ${friendlyMsg(e)}
|
|
4090
4133
|
`);
|
|
4091
4134
|
}
|
|
4092
4135
|
});
|
|
@@ -4100,7 +4143,7 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
4100
4143
|
if (Date.now() > pollDeadline) {
|
|
4101
4144
|
for (const [, f] of inFlight) {
|
|
4102
4145
|
failed++;
|
|
4103
|
-
process.stderr.write(` ${red("\u2717")} ${
|
|
4146
|
+
process.stderr.write(` ${red("\u2717")} ${path5.basename(f.path)} \u8F6E\u8BE2\u8D85\u65F6
|
|
4104
4147
|
`);
|
|
4105
4148
|
}
|
|
4106
4149
|
inFlight.clear();
|
|
@@ -4109,13 +4152,13 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
4109
4152
|
await Promise.all(
|
|
4110
4153
|
[...inFlight.entries()].map(async ([jobId, f]) => {
|
|
4111
4154
|
try {
|
|
4112
|
-
const s = await
|
|
4155
|
+
const s = await retryWith429(() => client.get("/ingest/status", { job_id: jobId }));
|
|
4113
4156
|
if (s.status !== "done" && s.status !== "failed") return;
|
|
4114
4157
|
inFlight.delete(jobId);
|
|
4115
4158
|
done++;
|
|
4116
4159
|
if (s.status === "failed") {
|
|
4117
4160
|
failed++;
|
|
4118
|
-
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"}
|
|
4119
4162
|
`);
|
|
4120
4163
|
return;
|
|
4121
4164
|
}
|
|
@@ -4124,7 +4167,7 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
4124
4167
|
saveManifest(manifest);
|
|
4125
4168
|
if (s.extracted === false) {
|
|
4126
4169
|
degraded++;
|
|
4127
|
-
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
|
|
4128
4171
|
`);
|
|
4129
4172
|
} else if (prev) {
|
|
4130
4173
|
updated++;
|
|
@@ -4172,15 +4215,22 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
4172
4215
|
}
|
|
4173
4216
|
|
|
4174
4217
|
// src/commands/ingest.ts
|
|
4218
|
+
function resolveIngestDir(dir, args) {
|
|
4219
|
+
const dirPath = typeof dir === "string" ? dir : args[0];
|
|
4220
|
+
if (!dirPath || dirPath === "-") {
|
|
4221
|
+
throw new Error("--dir \u9700\u8981\u76EE\u5F55\u8DEF\u5F84\u3002\u4E24\u79CD\u5199\u6CD5: exomind ingest --dir <\u76EE\u5F55> \u6216 exomind ingest <\u76EE\u5F55> --dir");
|
|
4222
|
+
}
|
|
4223
|
+
return dirPath;
|
|
4224
|
+
}
|
|
4175
4225
|
async function ingest(client, opts, args) {
|
|
4176
|
-
if (opts.dir) return runDirIngestest(client, opts, opts.dir);
|
|
4226
|
+
if (opts.dir) return runDirIngestest(client, opts, resolveIngestDir(opts.dir, args));
|
|
4177
4227
|
let content = "";
|
|
4178
4228
|
let fileAbs = null;
|
|
4179
4229
|
let fileRaw = null;
|
|
4180
4230
|
if (opts.file) {
|
|
4181
4231
|
fileRaw = readFileText(opts.file);
|
|
4182
4232
|
content = fileRaw;
|
|
4183
|
-
fileAbs =
|
|
4233
|
+
fileAbs = path6.resolve(opts.file);
|
|
4184
4234
|
} else if (args.length && args[0] === "-") {
|
|
4185
4235
|
content = await readStdinForced();
|
|
4186
4236
|
} else if (args.length) {
|
|
@@ -4202,7 +4252,7 @@ async function ingest(client, opts, args) {
|
|
|
4202
4252
|
const result = await ingestWithRetry(client, body, opTimeout(3e5));
|
|
4203
4253
|
if (fileAbs && fileRaw !== null) {
|
|
4204
4254
|
const man = loadManifest();
|
|
4205
|
-
recordFile(man, fileAbs, fileRaw, opts.title ||
|
|
4255
|
+
recordFile(man, fileAbs, fileRaw, opts.title || path6.basename(fileAbs));
|
|
4206
4256
|
saveManifest(man);
|
|
4207
4257
|
}
|
|
4208
4258
|
output(result, () => {
|
|
@@ -4227,7 +4277,7 @@ async function query(client, opts, args) {
|
|
|
4227
4277
|
if (opts.tag?.length) body.tags = opts.tag;
|
|
4228
4278
|
if (opts.model) body.model = opts.model;
|
|
4229
4279
|
hint("\u23F3 \u67E5\u8BE2\u4E2D: LLM \u68C0\u7D22 + \u751F\u6210,\u53EF\u80FD 1-2 \u5206\u949F\u2026");
|
|
4230
|
-
const result = await client.post("/query", body, { timeoutMs: opTimeout(18e4) });
|
|
4280
|
+
const result = await retryWith429(() => client.post("/query", body, { timeoutMs: opTimeout(18e4) }));
|
|
4231
4281
|
output(result, () => {
|
|
4232
4282
|
console.log(result.answer || dim("(\u65E0\u56DE\u7B54)"));
|
|
4233
4283
|
if (result.pages?.length) {
|
|
@@ -4242,12 +4292,12 @@ async function query(client, opts, args) {
|
|
|
4242
4292
|
async function search(client, opts, args) {
|
|
4243
4293
|
const keyword = args.join(" ").trim();
|
|
4244
4294
|
if (!keyword) throw new Error('\u8BF7\u63D0\u4F9B\u5173\u952E\u8BCD: exomind search "\u5173\u952E\u8BCD"');
|
|
4245
|
-
const result = await client.get("/search", {
|
|
4295
|
+
const result = await retryWith429(() => client.get("/search", {
|
|
4246
4296
|
q: keyword,
|
|
4247
4297
|
limit: opts.limit ?? 10,
|
|
4248
4298
|
rerank: opts.rerank ?? false,
|
|
4249
4299
|
hybrid: opts.hybrid ?? false
|
|
4250
|
-
});
|
|
4300
|
+
}));
|
|
4251
4301
|
const results = result.results || [];
|
|
4252
4302
|
output(result, () => {
|
|
4253
4303
|
if (!results.length) {
|
|
@@ -4260,7 +4310,8 @@ async function search(client, opts, args) {
|
|
|
4260
4310
|
console.log(`
|
|
4261
4311
|
${i + 1}. ${cyan(title)}`);
|
|
4262
4312
|
if (r.path) console.log(dim(` ${r.path}`));
|
|
4263
|
-
|
|
4313
|
+
const snip = r.snippet ?? r.content;
|
|
4314
|
+
if (snip) console.log(dim(` ${truncate(String(snip), 100)}`));
|
|
4264
4315
|
if (r.score != null) console.log(dim(` score: ${r.score}`));
|
|
4265
4316
|
});
|
|
4266
4317
|
});
|
|
@@ -4638,26 +4689,26 @@ async function whoami(client) {
|
|
|
4638
4689
|
}
|
|
4639
4690
|
|
|
4640
4691
|
// src/commands/install.ts
|
|
4641
|
-
var
|
|
4642
|
-
var
|
|
4692
|
+
var fs7 = __toESM(require("fs"));
|
|
4693
|
+
var path7 = __toESM(require("path"));
|
|
4643
4694
|
var os2 = __toESM(require("os"));
|
|
4644
4695
|
var import_node_child_process = require("child_process");
|
|
4645
|
-
var PKG_ROOT =
|
|
4646
|
-
var CLAUDE_SKILL_SRC =
|
|
4647
|
-
var CODEX_SKILL_SRC =
|
|
4696
|
+
var PKG_ROOT = path7.resolve(__dirname, "..");
|
|
4697
|
+
var CLAUDE_SKILL_SRC = path7.join(PKG_ROOT, "skill", "claude", "SKILL.md");
|
|
4698
|
+
var CODEX_SKILL_SRC = path7.join(PKG_ROOT, "skill", "codex", "SKILL.md");
|
|
4648
4699
|
function readJson2(file) {
|
|
4649
4700
|
try {
|
|
4650
|
-
return JSON.parse(
|
|
4701
|
+
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
4651
4702
|
} catch {
|
|
4652
4703
|
return null;
|
|
4653
4704
|
}
|
|
4654
4705
|
}
|
|
4655
4706
|
function backup(file) {
|
|
4656
|
-
if (!
|
|
4707
|
+
if (!fs7.existsSync(file)) return;
|
|
4657
4708
|
const bak = `${file}.bak-${Date.now()}`;
|
|
4658
4709
|
try {
|
|
4659
|
-
|
|
4660
|
-
console.log(dim(` (\u5DF2\u5907\u4EFD \u2192 ${
|
|
4710
|
+
fs7.copyFileSync(file, bak);
|
|
4711
|
+
console.log(dim(` (\u5DF2\u5907\u4EFD \u2192 ${path7.basename(bak)})`));
|
|
4661
4712
|
} catch {
|
|
4662
4713
|
return;
|
|
4663
4714
|
}
|
|
@@ -4665,12 +4716,12 @@ function backup(file) {
|
|
|
4665
4716
|
}
|
|
4666
4717
|
function pruneBackups(file, keep) {
|
|
4667
4718
|
try {
|
|
4668
|
-
const dir =
|
|
4669
|
-
const prefix =
|
|
4670
|
-
const olds =
|
|
4719
|
+
const dir = path7.dirname(file);
|
|
4720
|
+
const prefix = path7.basename(file) + ".bak-";
|
|
4721
|
+
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);
|
|
4671
4722
|
for (const o of olds.slice(keep)) {
|
|
4672
4723
|
try {
|
|
4673
|
-
|
|
4724
|
+
fs7.unlinkSync(path7.join(dir, o.f));
|
|
4674
4725
|
} catch {
|
|
4675
4726
|
}
|
|
4676
4727
|
}
|
|
@@ -4679,7 +4730,7 @@ function pruneBackups(file, keep) {
|
|
|
4679
4730
|
}
|
|
4680
4731
|
function resolveCodexHome(env = process.env, homeDir = os2.homedir()) {
|
|
4681
4732
|
const configured = (env.CODEX_HOME ?? "").trim();
|
|
4682
|
-
return configured ?
|
|
4733
|
+
return configured ? path7.resolve(configured) : path7.join(homeDir, ".codex");
|
|
4683
4734
|
}
|
|
4684
4735
|
function looksLikeValidToml(text) {
|
|
4685
4736
|
if (!text.trim()) return true;
|
|
@@ -4693,14 +4744,14 @@ function looksLikeValidToml(text) {
|
|
|
4693
4744
|
return brackets === 0;
|
|
4694
4745
|
}
|
|
4695
4746
|
function configureCodexMcp(codexHome) {
|
|
4696
|
-
const codexConfig =
|
|
4747
|
+
const codexConfig = path7.join(codexHome, "config.toml");
|
|
4697
4748
|
try {
|
|
4698
|
-
|
|
4749
|
+
fs7.mkdirSync(path7.dirname(codexConfig), { recursive: true });
|
|
4699
4750
|
} catch {
|
|
4700
4751
|
}
|
|
4701
4752
|
let existing = "";
|
|
4702
4753
|
try {
|
|
4703
|
-
existing =
|
|
4754
|
+
existing = fs7.readFileSync(codexConfig, "utf-8");
|
|
4704
4755
|
} catch {
|
|
4705
4756
|
existing = "";
|
|
4706
4757
|
}
|
|
@@ -4721,24 +4772,24 @@ function configureCodexMcp(codexHome) {
|
|
|
4721
4772
|
command = "exomind"
|
|
4722
4773
|
args = ["mcp"]
|
|
4723
4774
|
`;
|
|
4724
|
-
|
|
4725
|
-
const after =
|
|
4775
|
+
fs7.writeFileSync(codexConfig, existing + snippet);
|
|
4776
|
+
const after = fs7.readFileSync(codexConfig, "utf-8");
|
|
4726
4777
|
if (!/^\[mcp_servers\.exomind\]\s*$/m.test(after) || !looksLikeValidToml(after)) {
|
|
4727
4778
|
return { ok: false, written: true, file: codexConfig, reason: "\u5199\u540E\u590D\u9A8C\u5931\u8D25(\u6BB5\u672A\u5199\u5165\u6216\u6587\u4EF6\u635F\u574F)" };
|
|
4728
4779
|
}
|
|
4729
4780
|
return { ok: true, written: true, file: codexConfig };
|
|
4730
4781
|
}
|
|
4731
4782
|
function installCodexSkill(codexHome, skillSrc = CODEX_SKILL_SRC) {
|
|
4732
|
-
const destDir =
|
|
4733
|
-
const dest =
|
|
4734
|
-
if (!
|
|
4783
|
+
const destDir = path7.join(codexHome, "skills", "exomind");
|
|
4784
|
+
const dest = path7.join(destDir, "SKILL.md");
|
|
4785
|
+
if (!fs7.existsSync(skillSrc)) {
|
|
4735
4786
|
return { ok: false, dest, reason: `skill \u6E90\u4E0D\u5B58\u5728: ${skillSrc}` };
|
|
4736
4787
|
}
|
|
4737
|
-
|
|
4788
|
+
fs7.mkdirSync(destDir, { recursive: true });
|
|
4738
4789
|
backup(dest);
|
|
4739
4790
|
const tmp = `${dest}.tmp-${Date.now()}`;
|
|
4740
|
-
|
|
4741
|
-
|
|
4791
|
+
fs7.writeFileSync(tmp, fs7.readFileSync(skillSrc));
|
|
4792
|
+
fs7.renameSync(tmp, dest);
|
|
4742
4793
|
return { ok: true, dest };
|
|
4743
4794
|
}
|
|
4744
4795
|
function purgeMcpExomind(file) {
|
|
@@ -4749,7 +4800,7 @@ function purgeMcpExomind(file) {
|
|
|
4749
4800
|
delete grp.exomind;
|
|
4750
4801
|
if (Object.keys(grp).length === 0) delete d.mcpServers;
|
|
4751
4802
|
backup(file);
|
|
4752
|
-
|
|
4803
|
+
fs7.writeFileSync(file, JSON.stringify(d, null, 2) + "\n");
|
|
4753
4804
|
return true;
|
|
4754
4805
|
}
|
|
4755
4806
|
function checkMcp(timeoutMs = 6e3) {
|
|
@@ -4801,17 +4852,17 @@ async function install(client, opts) {
|
|
|
4801
4852
|
if (opts.host && !["claude", "codex", "opencode"].includes(opts.host)) {
|
|
4802
4853
|
throw new Error(`\u672A\u77E5 --host: ${opts.host}(\u53EF\u9009: claude | codex | opencode)`);
|
|
4803
4854
|
}
|
|
4804
|
-
const claudeDir =
|
|
4805
|
-
const skillDestDir =
|
|
4855
|
+
const claudeDir = path7.join(os2.homedir(), ".claude");
|
|
4856
|
+
const skillDestDir = path7.join(claudeDir, "skills", "exomind");
|
|
4806
4857
|
const codexHome = resolveCodexHome();
|
|
4807
4858
|
const want = (h) => !opts.host || opts.host === h;
|
|
4808
4859
|
if (opts.skill !== false) {
|
|
4809
4860
|
if (want("claude")) {
|
|
4810
|
-
if (!
|
|
4861
|
+
if (!fs7.existsSync(CLAUDE_SKILL_SRC)) {
|
|
4811
4862
|
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)`);
|
|
4812
4863
|
}
|
|
4813
|
-
|
|
4814
|
-
|
|
4864
|
+
fs7.mkdirSync(skillDestDir, { recursive: true });
|
|
4865
|
+
fs7.copyFileSync(CLAUDE_SKILL_SRC, path7.join(skillDestDir, "SKILL.md"));
|
|
4815
4866
|
console.log(ok("\u5DF2\u5B89\u88C5 Claude Code skill"));
|
|
4816
4867
|
console.log(dim(` \u2192 ${skillDestDir}/SKILL.md`));
|
|
4817
4868
|
}
|
|
@@ -4826,8 +4877,8 @@ async function install(client, opts) {
|
|
|
4826
4877
|
}
|
|
4827
4878
|
}
|
|
4828
4879
|
if (opts.hook !== false && want("claude")) {
|
|
4829
|
-
const settingsFile =
|
|
4830
|
-
|
|
4880
|
+
const settingsFile = path7.join(claudeDir, "settings.json");
|
|
4881
|
+
fs7.mkdirSync(claudeDir, { recursive: true });
|
|
4831
4882
|
backup(settingsFile);
|
|
4832
4883
|
const settings = readJson2(settingsFile) ?? {};
|
|
4833
4884
|
const hooks = settings.hooks ?? {};
|
|
@@ -4840,31 +4891,31 @@ async function install(client, opts) {
|
|
|
4840
4891
|
});
|
|
4841
4892
|
hooks.UserPromptSubmit = kept;
|
|
4842
4893
|
settings.hooks = hooks;
|
|
4843
|
-
|
|
4894
|
+
fs7.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + "\n");
|
|
4844
4895
|
console.log(ok("\u5DF2\u914D\u7F6E UserPromptSubmit hook \u2192 exomind hook (Claude Code \u72EC\u6709)"));
|
|
4845
4896
|
console.log(dim(" Codex \u5F53\u524D\u65E0\u5BF9\u5E94 prompt-submit hook,\u9760 skill \u89E6\u53D1(jdit/\u5B58\u6863/\u67E5\u8BE2)\u3002"));
|
|
4846
4897
|
}
|
|
4847
4898
|
if (opts.mcp !== false) {
|
|
4848
4899
|
const configured = [];
|
|
4849
4900
|
if (want("claude")) {
|
|
4850
|
-
const ccJson =
|
|
4901
|
+
const ccJson = path7.join(os2.homedir(), ".claude.json");
|
|
4851
4902
|
backup(ccJson);
|
|
4852
4903
|
const cc = readJson2(ccJson) ?? {};
|
|
4853
4904
|
const ccMcp = cc.mcpServers ?? {};
|
|
4854
4905
|
ccMcp.exomind = { command: "exomind", args: ["mcp"] };
|
|
4855
4906
|
cc.mcpServers = ccMcp;
|
|
4856
|
-
|
|
4907
|
+
fs7.writeFileSync(ccJson, JSON.stringify(cc, null, 2) + "\n");
|
|
4857
4908
|
configured.push("Claude Code (~/.claude.json)");
|
|
4858
4909
|
}
|
|
4859
4910
|
if (want("opencode")) {
|
|
4860
|
-
const ocJson =
|
|
4861
|
-
|
|
4911
|
+
const ocJson = path7.join(os2.homedir(), ".config", "opencode", "opencode.json");
|
|
4912
|
+
fs7.mkdirSync(path7.dirname(ocJson), { recursive: true });
|
|
4862
4913
|
backup(ocJson);
|
|
4863
4914
|
const oc = readJson2(ocJson) ?? {};
|
|
4864
4915
|
const ocMcp = oc.mcp ?? {};
|
|
4865
4916
|
ocMcp.exomind = { type: "local", command: ["exomind", "mcp"] };
|
|
4866
4917
|
oc.mcp = ocMcp;
|
|
4867
|
-
|
|
4918
|
+
fs7.writeFileSync(ocJson, JSON.stringify(oc, null, 2) + "\n");
|
|
4868
4919
|
configured.push("OpenCode (~/.config/opencode/opencode.json)");
|
|
4869
4920
|
}
|
|
4870
4921
|
if (want("codex")) {
|
|
@@ -4873,14 +4924,14 @@ async function install(client, opts) {
|
|
|
4873
4924
|
else console.log(yellow(`\u2717 Codex MCP \u914D\u7F6E\u5931\u8D25: ${r.reason}`));
|
|
4874
4925
|
}
|
|
4875
4926
|
const purged = [];
|
|
4876
|
-
if (want("claude") && purgeMcpExomind(
|
|
4927
|
+
if (want("claude") && purgeMcpExomind(path7.join(claudeDir, "settings.json"))) {
|
|
4877
4928
|
purged.push("~/.claude/settings.json");
|
|
4878
4929
|
}
|
|
4879
4930
|
let curDir = process.cwd();
|
|
4880
|
-
for (let i = 0; i < 12 && curDir !==
|
|
4881
|
-
const f =
|
|
4882
|
-
if (
|
|
4883
|
-
curDir =
|
|
4931
|
+
for (let i = 0; i < 12 && curDir !== path7.dirname(curDir); i++) {
|
|
4932
|
+
const f = path7.join(curDir, ".mcp.json");
|
|
4933
|
+
if (fs7.existsSync(f) && purgeMcpExomind(f)) purged.push(f.replace(os2.homedir(), "~"));
|
|
4934
|
+
curDir = path7.dirname(curDir);
|
|
4884
4935
|
}
|
|
4885
4936
|
if (purged.length) console.log(dim(` \u2192 \u6E05\u7406\u6B8B\u7559 exomind MCP \u6761\u76EE(\u907F\u514D\u8986\u76D6 stdio): ${purged.join(", ")}`));
|
|
4886
4937
|
if (configured.length) {
|
|
@@ -4889,12 +4940,12 @@ async function install(client, opts) {
|
|
|
4889
4940
|
console.log(dim(" \u91CD\u542F\u5BF9\u5E94 Agent \u2192 \u62FF\u5230 mcp__exomind__* \u5DE5\u5177"));
|
|
4890
4941
|
}
|
|
4891
4942
|
}
|
|
4892
|
-
const cfgFile =
|
|
4943
|
+
const cfgFile = path7.join(os2.homedir(), ".exomind", "config.json");
|
|
4893
4944
|
const cfg = readJson2(cfgFile);
|
|
4894
4945
|
if (cfg && typeof cfg.base_url === "string" && cfg.base_url.includes("d.youhuale.cn")) {
|
|
4895
4946
|
backup(cfgFile);
|
|
4896
4947
|
cfg.base_url = DEFAULT_BASE_URL;
|
|
4897
|
-
|
|
4948
|
+
fs7.writeFileSync(cfgFile, JSON.stringify(cfg, null, 2) + "\n");
|
|
4898
4949
|
console.log(ok(`\u5DF2\u8FC1\u79FB base_url: d.youhuale.cn \u2192 ${DEFAULT_BASE_URL}`));
|
|
4899
4950
|
console.log(dim(" (stdio MCP \u5B50\u8FDB\u7A0B\u8BFB\u6B64\u914D\u7F6E,\u91CD\u542F Agent \u540E\u7528\u65B0\u57DF\u540D)"));
|
|
4900
4951
|
}
|
|
@@ -4913,10 +4964,10 @@ async function install(client, opts) {
|
|
|
4913
4964
|
}
|
|
4914
4965
|
}
|
|
4915
4966
|
const mcp = live.api_key ? await checkMcp() : { ok: false, detail: "\u672A\u914D\u7F6E API Key" };
|
|
4916
|
-
const codexSkillOk =
|
|
4967
|
+
const codexSkillOk = fs7.existsSync(path7.join(codexHome, "skills", "exomind", "SKILL.md"));
|
|
4917
4968
|
const codexMcfgOk = (() => {
|
|
4918
4969
|
try {
|
|
4919
|
-
return /^\[mcp_servers\.exomind\]\s*$/m.test(
|
|
4970
|
+
return /^\[mcp_servers\.exomind\]\s*$/m.test(fs7.readFileSync(path7.join(codexHome, "config.toml"), "utf-8"));
|
|
4920
4971
|
} catch {
|
|
4921
4972
|
return false;
|
|
4922
4973
|
}
|
|
@@ -4947,19 +4998,19 @@ async function install(client, opts) {
|
|
|
4947
4998
|
}
|
|
4948
4999
|
|
|
4949
5000
|
// src/commands/doctor.ts
|
|
4950
|
-
var
|
|
4951
|
-
var
|
|
5001
|
+
var fs8 = __toESM(require("fs"));
|
|
5002
|
+
var path8 = __toESM(require("path"));
|
|
4952
5003
|
var os3 = __toESM(require("os"));
|
|
4953
5004
|
function readText(p) {
|
|
4954
5005
|
try {
|
|
4955
|
-
return
|
|
5006
|
+
return fs8.readFileSync(p, "utf-8");
|
|
4956
5007
|
} catch {
|
|
4957
5008
|
return "";
|
|
4958
5009
|
}
|
|
4959
5010
|
}
|
|
4960
5011
|
function fileExists(p) {
|
|
4961
5012
|
try {
|
|
4962
|
-
return
|
|
5013
|
+
return fs8.existsSync(p);
|
|
4963
5014
|
} catch {
|
|
4964
5015
|
return false;
|
|
4965
5016
|
}
|
|
@@ -4982,9 +5033,9 @@ function badge(s) {
|
|
|
4982
5033
|
async function doctor(client) {
|
|
4983
5034
|
const codexHome = resolveCodexHome();
|
|
4984
5035
|
const checks = [];
|
|
4985
|
-
const claudeSkill =
|
|
4986
|
-
const claudeSettings =
|
|
4987
|
-
const claudeJson =
|
|
5036
|
+
const claudeSkill = path8.join(os3.homedir(), ".claude", "skills", "exomind", "SKILL.md");
|
|
5037
|
+
const claudeSettings = path8.join(os3.homedir(), ".claude", "settings.json");
|
|
5038
|
+
const claudeJson = path8.join(os3.homedir(), ".claude.json");
|
|
4988
5039
|
checks.push({
|
|
4989
5040
|
host: "claude",
|
|
4990
5041
|
skill: fileExists(claudeSkill) ? "ok" : "missing",
|
|
@@ -4992,8 +5043,8 @@ async function doctor(client) {
|
|
|
4992
5043
|
mcpConfig: mcpServersExomind(claudeJson, "mcpServers") ? "ok" : "missing",
|
|
4993
5044
|
paths: [claudeSkill, claudeSettings, claudeJson]
|
|
4994
5045
|
});
|
|
4995
|
-
const codexSkill =
|
|
4996
|
-
const codexConfig =
|
|
5046
|
+
const codexSkill = path8.join(codexHome, "skills", "exomind", "SKILL.md");
|
|
5047
|
+
const codexConfig = path8.join(codexHome, "config.toml");
|
|
4997
5048
|
checks.push({
|
|
4998
5049
|
host: "codex",
|
|
4999
5050
|
skill: fileExists(codexSkill) ? "ok" : "missing",
|
|
@@ -5002,7 +5053,7 @@ async function doctor(client) {
|
|
|
5002
5053
|
paths: [codexSkill, codexConfig],
|
|
5003
5054
|
detail: "Codex \u65E0 prompt-submit hook,\u9760 skill \u89E6\u53D1(jdit/\u5B58\u6863/\u67E5\u8BE2)"
|
|
5004
5055
|
});
|
|
5005
|
-
const ocJson =
|
|
5056
|
+
const ocJson = path8.join(os3.homedir(), ".config", "opencode", "opencode.json");
|
|
5006
5057
|
checks.push({
|
|
5007
5058
|
host: "opencode",
|
|
5008
5059
|
skill: "unsupported",
|
|
@@ -5080,6 +5131,7 @@ function run(fn) {
|
|
|
5080
5131
|
const client = new ApiClient(cfg);
|
|
5081
5132
|
try {
|
|
5082
5133
|
await fn(client, command.opts(), command.args);
|
|
5134
|
+
if (!root.json) await maybeNotifyUpdate(VERSION);
|
|
5083
5135
|
} catch (e) {
|
|
5084
5136
|
handleError(e);
|
|
5085
5137
|
}
|
|
@@ -5090,7 +5142,7 @@ program2.name("exomind").description("ExoMind \u8DE8\u5E73\u53F0\u77E5\u8BC6\u5E
|
|
|
5090
5142
|
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));
|
|
5091
5143
|
program2.command("me").description("\u663E\u793A\u5F53\u524D\u767B\u5F55\u6001\u4E0E\u670D\u52A1\u5668").action(run(whoami));
|
|
5092
5144
|
program2.command("whoami", { hidden: true }).description("\u663E\u793A\u5F53\u524D\u767B\u5F55\u6001\u4E0E\u670D\u52A1\u5668(me \u7684\u65E7\u540D)").action(run(whoami));
|
|
5093
|
-
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
|
|
5145
|
+
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;\u76EE\u5F55\u53EF\u5199\u5728\u524D: ingest <\u76EE\u5F55> --dir)").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)").option(
|
|
5094
5146
|
"-c, --concurrency <n>",
|
|
5095
5147
|
"\u5E76\u53D1\u6444\u5165\u6570(\u914D\u5408 --dir,\u9ED8\u8BA4 3,\u5F31\u670D\u52A1\u5668\u53CB\u597D)",
|
|
5096
5148
|
(v) => parseInt(v, 10) || 3,
|