exomind 0.9.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 +154 -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"
|
|
@@ -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++;
|
|
@@ -4180,7 +4223,7 @@ async function ingest(client, opts, args) {
|
|
|
4180
4223
|
if (opts.file) {
|
|
4181
4224
|
fileRaw = readFileText(opts.file);
|
|
4182
4225
|
content = fileRaw;
|
|
4183
|
-
fileAbs =
|
|
4226
|
+
fileAbs = path6.resolve(opts.file);
|
|
4184
4227
|
} else if (args.length && args[0] === "-") {
|
|
4185
4228
|
content = await readStdinForced();
|
|
4186
4229
|
} else if (args.length) {
|
|
@@ -4202,7 +4245,7 @@ async function ingest(client, opts, args) {
|
|
|
4202
4245
|
const result = await ingestWithRetry(client, body, opTimeout(3e5));
|
|
4203
4246
|
if (fileAbs && fileRaw !== null) {
|
|
4204
4247
|
const man = loadManifest();
|
|
4205
|
-
recordFile(man, fileAbs, fileRaw, opts.title ||
|
|
4248
|
+
recordFile(man, fileAbs, fileRaw, opts.title || path6.basename(fileAbs));
|
|
4206
4249
|
saveManifest(man);
|
|
4207
4250
|
}
|
|
4208
4251
|
output(result, () => {
|
|
@@ -4227,7 +4270,7 @@ async function query(client, opts, args) {
|
|
|
4227
4270
|
if (opts.tag?.length) body.tags = opts.tag;
|
|
4228
4271
|
if (opts.model) body.model = opts.model;
|
|
4229
4272
|
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) });
|
|
4273
|
+
const result = await retryWith429(() => client.post("/query", body, { timeoutMs: opTimeout(18e4) }));
|
|
4231
4274
|
output(result, () => {
|
|
4232
4275
|
console.log(result.answer || dim("(\u65E0\u56DE\u7B54)"));
|
|
4233
4276
|
if (result.pages?.length) {
|
|
@@ -4242,12 +4285,12 @@ async function query(client, opts, args) {
|
|
|
4242
4285
|
async function search(client, opts, args) {
|
|
4243
4286
|
const keyword = args.join(" ").trim();
|
|
4244
4287
|
if (!keyword) throw new Error('\u8BF7\u63D0\u4F9B\u5173\u952E\u8BCD: exomind search "\u5173\u952E\u8BCD"');
|
|
4245
|
-
const result = await client.get("/search", {
|
|
4288
|
+
const result = await retryWith429(() => client.get("/search", {
|
|
4246
4289
|
q: keyword,
|
|
4247
4290
|
limit: opts.limit ?? 10,
|
|
4248
4291
|
rerank: opts.rerank ?? false,
|
|
4249
4292
|
hybrid: opts.hybrid ?? false
|
|
4250
|
-
});
|
|
4293
|
+
}));
|
|
4251
4294
|
const results = result.results || [];
|
|
4252
4295
|
output(result, () => {
|
|
4253
4296
|
if (!results.length) {
|
|
@@ -4638,26 +4681,26 @@ async function whoami(client) {
|
|
|
4638
4681
|
}
|
|
4639
4682
|
|
|
4640
4683
|
// src/commands/install.ts
|
|
4641
|
-
var
|
|
4642
|
-
var
|
|
4684
|
+
var fs7 = __toESM(require("fs"));
|
|
4685
|
+
var path7 = __toESM(require("path"));
|
|
4643
4686
|
var os2 = __toESM(require("os"));
|
|
4644
4687
|
var import_node_child_process = require("child_process");
|
|
4645
|
-
var PKG_ROOT =
|
|
4646
|
-
var CLAUDE_SKILL_SRC =
|
|
4647
|
-
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");
|
|
4648
4691
|
function readJson2(file) {
|
|
4649
4692
|
try {
|
|
4650
|
-
return JSON.parse(
|
|
4693
|
+
return JSON.parse(fs7.readFileSync(file, "utf-8"));
|
|
4651
4694
|
} catch {
|
|
4652
4695
|
return null;
|
|
4653
4696
|
}
|
|
4654
4697
|
}
|
|
4655
4698
|
function backup(file) {
|
|
4656
|
-
if (!
|
|
4699
|
+
if (!fs7.existsSync(file)) return;
|
|
4657
4700
|
const bak = `${file}.bak-${Date.now()}`;
|
|
4658
4701
|
try {
|
|
4659
|
-
|
|
4660
|
-
console.log(dim(` (\u5DF2\u5907\u4EFD \u2192 ${
|
|
4702
|
+
fs7.copyFileSync(file, bak);
|
|
4703
|
+
console.log(dim(` (\u5DF2\u5907\u4EFD \u2192 ${path7.basename(bak)})`));
|
|
4661
4704
|
} catch {
|
|
4662
4705
|
return;
|
|
4663
4706
|
}
|
|
@@ -4665,12 +4708,12 @@ function backup(file) {
|
|
|
4665
4708
|
}
|
|
4666
4709
|
function pruneBackups(file, keep) {
|
|
4667
4710
|
try {
|
|
4668
|
-
const dir =
|
|
4669
|
-
const prefix =
|
|
4670
|
-
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);
|
|
4671
4714
|
for (const o of olds.slice(keep)) {
|
|
4672
4715
|
try {
|
|
4673
|
-
|
|
4716
|
+
fs7.unlinkSync(path7.join(dir, o.f));
|
|
4674
4717
|
} catch {
|
|
4675
4718
|
}
|
|
4676
4719
|
}
|
|
@@ -4679,7 +4722,7 @@ function pruneBackups(file, keep) {
|
|
|
4679
4722
|
}
|
|
4680
4723
|
function resolveCodexHome(env = process.env, homeDir = os2.homedir()) {
|
|
4681
4724
|
const configured = (env.CODEX_HOME ?? "").trim();
|
|
4682
|
-
return configured ?
|
|
4725
|
+
return configured ? path7.resolve(configured) : path7.join(homeDir, ".codex");
|
|
4683
4726
|
}
|
|
4684
4727
|
function looksLikeValidToml(text) {
|
|
4685
4728
|
if (!text.trim()) return true;
|
|
@@ -4693,14 +4736,14 @@ function looksLikeValidToml(text) {
|
|
|
4693
4736
|
return brackets === 0;
|
|
4694
4737
|
}
|
|
4695
4738
|
function configureCodexMcp(codexHome) {
|
|
4696
|
-
const codexConfig =
|
|
4739
|
+
const codexConfig = path7.join(codexHome, "config.toml");
|
|
4697
4740
|
try {
|
|
4698
|
-
|
|
4741
|
+
fs7.mkdirSync(path7.dirname(codexConfig), { recursive: true });
|
|
4699
4742
|
} catch {
|
|
4700
4743
|
}
|
|
4701
4744
|
let existing = "";
|
|
4702
4745
|
try {
|
|
4703
|
-
existing =
|
|
4746
|
+
existing = fs7.readFileSync(codexConfig, "utf-8");
|
|
4704
4747
|
} catch {
|
|
4705
4748
|
existing = "";
|
|
4706
4749
|
}
|
|
@@ -4721,24 +4764,24 @@ function configureCodexMcp(codexHome) {
|
|
|
4721
4764
|
command = "exomind"
|
|
4722
4765
|
args = ["mcp"]
|
|
4723
4766
|
`;
|
|
4724
|
-
|
|
4725
|
-
const after =
|
|
4767
|
+
fs7.writeFileSync(codexConfig, existing + snippet);
|
|
4768
|
+
const after = fs7.readFileSync(codexConfig, "utf-8");
|
|
4726
4769
|
if (!/^\[mcp_servers\.exomind\]\s*$/m.test(after) || !looksLikeValidToml(after)) {
|
|
4727
4770
|
return { ok: false, written: true, file: codexConfig, reason: "\u5199\u540E\u590D\u9A8C\u5931\u8D25(\u6BB5\u672A\u5199\u5165\u6216\u6587\u4EF6\u635F\u574F)" };
|
|
4728
4771
|
}
|
|
4729
4772
|
return { ok: true, written: true, file: codexConfig };
|
|
4730
4773
|
}
|
|
4731
4774
|
function installCodexSkill(codexHome, skillSrc = CODEX_SKILL_SRC) {
|
|
4732
|
-
const destDir =
|
|
4733
|
-
const dest =
|
|
4734
|
-
if (!
|
|
4775
|
+
const destDir = path7.join(codexHome, "skills", "exomind");
|
|
4776
|
+
const dest = path7.join(destDir, "SKILL.md");
|
|
4777
|
+
if (!fs7.existsSync(skillSrc)) {
|
|
4735
4778
|
return { ok: false, dest, reason: `skill \u6E90\u4E0D\u5B58\u5728: ${skillSrc}` };
|
|
4736
4779
|
}
|
|
4737
|
-
|
|
4780
|
+
fs7.mkdirSync(destDir, { recursive: true });
|
|
4738
4781
|
backup(dest);
|
|
4739
4782
|
const tmp = `${dest}.tmp-${Date.now()}`;
|
|
4740
|
-
|
|
4741
|
-
|
|
4783
|
+
fs7.writeFileSync(tmp, fs7.readFileSync(skillSrc));
|
|
4784
|
+
fs7.renameSync(tmp, dest);
|
|
4742
4785
|
return { ok: true, dest };
|
|
4743
4786
|
}
|
|
4744
4787
|
function purgeMcpExomind(file) {
|
|
@@ -4749,7 +4792,7 @@ function purgeMcpExomind(file) {
|
|
|
4749
4792
|
delete grp.exomind;
|
|
4750
4793
|
if (Object.keys(grp).length === 0) delete d.mcpServers;
|
|
4751
4794
|
backup(file);
|
|
4752
|
-
|
|
4795
|
+
fs7.writeFileSync(file, JSON.stringify(d, null, 2) + "\n");
|
|
4753
4796
|
return true;
|
|
4754
4797
|
}
|
|
4755
4798
|
function checkMcp(timeoutMs = 6e3) {
|
|
@@ -4801,17 +4844,17 @@ async function install(client, opts) {
|
|
|
4801
4844
|
if (opts.host && !["claude", "codex", "opencode"].includes(opts.host)) {
|
|
4802
4845
|
throw new Error(`\u672A\u77E5 --host: ${opts.host}(\u53EF\u9009: claude | codex | opencode)`);
|
|
4803
4846
|
}
|
|
4804
|
-
const claudeDir =
|
|
4805
|
-
const skillDestDir =
|
|
4847
|
+
const claudeDir = path7.join(os2.homedir(), ".claude");
|
|
4848
|
+
const skillDestDir = path7.join(claudeDir, "skills", "exomind");
|
|
4806
4849
|
const codexHome = resolveCodexHome();
|
|
4807
4850
|
const want = (h) => !opts.host || opts.host === h;
|
|
4808
4851
|
if (opts.skill !== false) {
|
|
4809
4852
|
if (want("claude")) {
|
|
4810
|
-
if (!
|
|
4853
|
+
if (!fs7.existsSync(CLAUDE_SKILL_SRC)) {
|
|
4811
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)`);
|
|
4812
4855
|
}
|
|
4813
|
-
|
|
4814
|
-
|
|
4856
|
+
fs7.mkdirSync(skillDestDir, { recursive: true });
|
|
4857
|
+
fs7.copyFileSync(CLAUDE_SKILL_SRC, path7.join(skillDestDir, "SKILL.md"));
|
|
4815
4858
|
console.log(ok("\u5DF2\u5B89\u88C5 Claude Code skill"));
|
|
4816
4859
|
console.log(dim(` \u2192 ${skillDestDir}/SKILL.md`));
|
|
4817
4860
|
}
|
|
@@ -4826,8 +4869,8 @@ async function install(client, opts) {
|
|
|
4826
4869
|
}
|
|
4827
4870
|
}
|
|
4828
4871
|
if (opts.hook !== false && want("claude")) {
|
|
4829
|
-
const settingsFile =
|
|
4830
|
-
|
|
4872
|
+
const settingsFile = path7.join(claudeDir, "settings.json");
|
|
4873
|
+
fs7.mkdirSync(claudeDir, { recursive: true });
|
|
4831
4874
|
backup(settingsFile);
|
|
4832
4875
|
const settings = readJson2(settingsFile) ?? {};
|
|
4833
4876
|
const hooks = settings.hooks ?? {};
|
|
@@ -4840,31 +4883,31 @@ async function install(client, opts) {
|
|
|
4840
4883
|
});
|
|
4841
4884
|
hooks.UserPromptSubmit = kept;
|
|
4842
4885
|
settings.hooks = hooks;
|
|
4843
|
-
|
|
4886
|
+
fs7.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + "\n");
|
|
4844
4887
|
console.log(ok("\u5DF2\u914D\u7F6E UserPromptSubmit hook \u2192 exomind hook (Claude Code \u72EC\u6709)"));
|
|
4845
4888
|
console.log(dim(" Codex \u5F53\u524D\u65E0\u5BF9\u5E94 prompt-submit hook,\u9760 skill \u89E6\u53D1(jdit/\u5B58\u6863/\u67E5\u8BE2)\u3002"));
|
|
4846
4889
|
}
|
|
4847
4890
|
if (opts.mcp !== false) {
|
|
4848
4891
|
const configured = [];
|
|
4849
4892
|
if (want("claude")) {
|
|
4850
|
-
const ccJson =
|
|
4893
|
+
const ccJson = path7.join(os2.homedir(), ".claude.json");
|
|
4851
4894
|
backup(ccJson);
|
|
4852
4895
|
const cc = readJson2(ccJson) ?? {};
|
|
4853
4896
|
const ccMcp = cc.mcpServers ?? {};
|
|
4854
4897
|
ccMcp.exomind = { command: "exomind", args: ["mcp"] };
|
|
4855
4898
|
cc.mcpServers = ccMcp;
|
|
4856
|
-
|
|
4899
|
+
fs7.writeFileSync(ccJson, JSON.stringify(cc, null, 2) + "\n");
|
|
4857
4900
|
configured.push("Claude Code (~/.claude.json)");
|
|
4858
4901
|
}
|
|
4859
4902
|
if (want("opencode")) {
|
|
4860
|
-
const ocJson =
|
|
4861
|
-
|
|
4903
|
+
const ocJson = path7.join(os2.homedir(), ".config", "opencode", "opencode.json");
|
|
4904
|
+
fs7.mkdirSync(path7.dirname(ocJson), { recursive: true });
|
|
4862
4905
|
backup(ocJson);
|
|
4863
4906
|
const oc = readJson2(ocJson) ?? {};
|
|
4864
4907
|
const ocMcp = oc.mcp ?? {};
|
|
4865
4908
|
ocMcp.exomind = { type: "local", command: ["exomind", "mcp"] };
|
|
4866
4909
|
oc.mcp = ocMcp;
|
|
4867
|
-
|
|
4910
|
+
fs7.writeFileSync(ocJson, JSON.stringify(oc, null, 2) + "\n");
|
|
4868
4911
|
configured.push("OpenCode (~/.config/opencode/opencode.json)");
|
|
4869
4912
|
}
|
|
4870
4913
|
if (want("codex")) {
|
|
@@ -4873,14 +4916,14 @@ async function install(client, opts) {
|
|
|
4873
4916
|
else console.log(yellow(`\u2717 Codex MCP \u914D\u7F6E\u5931\u8D25: ${r.reason}`));
|
|
4874
4917
|
}
|
|
4875
4918
|
const purged = [];
|
|
4876
|
-
if (want("claude") && purgeMcpExomind(
|
|
4919
|
+
if (want("claude") && purgeMcpExomind(path7.join(claudeDir, "settings.json"))) {
|
|
4877
4920
|
purged.push("~/.claude/settings.json");
|
|
4878
4921
|
}
|
|
4879
4922
|
let curDir = process.cwd();
|
|
4880
|
-
for (let i = 0; i < 12 && curDir !==
|
|
4881
|
-
const f =
|
|
4882
|
-
if (
|
|
4883
|
-
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);
|
|
4884
4927
|
}
|
|
4885
4928
|
if (purged.length) console.log(dim(` \u2192 \u6E05\u7406\u6B8B\u7559 exomind MCP \u6761\u76EE(\u907F\u514D\u8986\u76D6 stdio): ${purged.join(", ")}`));
|
|
4886
4929
|
if (configured.length) {
|
|
@@ -4889,12 +4932,12 @@ async function install(client, opts) {
|
|
|
4889
4932
|
console.log(dim(" \u91CD\u542F\u5BF9\u5E94 Agent \u2192 \u62FF\u5230 mcp__exomind__* \u5DE5\u5177"));
|
|
4890
4933
|
}
|
|
4891
4934
|
}
|
|
4892
|
-
const cfgFile =
|
|
4935
|
+
const cfgFile = path7.join(os2.homedir(), ".exomind", "config.json");
|
|
4893
4936
|
const cfg = readJson2(cfgFile);
|
|
4894
4937
|
if (cfg && typeof cfg.base_url === "string" && cfg.base_url.includes("d.youhuale.cn")) {
|
|
4895
4938
|
backup(cfgFile);
|
|
4896
4939
|
cfg.base_url = DEFAULT_BASE_URL;
|
|
4897
|
-
|
|
4940
|
+
fs7.writeFileSync(cfgFile, JSON.stringify(cfg, null, 2) + "\n");
|
|
4898
4941
|
console.log(ok(`\u5DF2\u8FC1\u79FB base_url: d.youhuale.cn \u2192 ${DEFAULT_BASE_URL}`));
|
|
4899
4942
|
console.log(dim(" (stdio MCP \u5B50\u8FDB\u7A0B\u8BFB\u6B64\u914D\u7F6E,\u91CD\u542F Agent \u540E\u7528\u65B0\u57DF\u540D)"));
|
|
4900
4943
|
}
|
|
@@ -4913,10 +4956,10 @@ async function install(client, opts) {
|
|
|
4913
4956
|
}
|
|
4914
4957
|
}
|
|
4915
4958
|
const mcp = live.api_key ? await checkMcp() : { ok: false, detail: "\u672A\u914D\u7F6E API Key" };
|
|
4916
|
-
const codexSkillOk =
|
|
4959
|
+
const codexSkillOk = fs7.existsSync(path7.join(codexHome, "skills", "exomind", "SKILL.md"));
|
|
4917
4960
|
const codexMcfgOk = (() => {
|
|
4918
4961
|
try {
|
|
4919
|
-
return /^\[mcp_servers\.exomind\]\s*$/m.test(
|
|
4962
|
+
return /^\[mcp_servers\.exomind\]\s*$/m.test(fs7.readFileSync(path7.join(codexHome, "config.toml"), "utf-8"));
|
|
4920
4963
|
} catch {
|
|
4921
4964
|
return false;
|
|
4922
4965
|
}
|
|
@@ -4947,19 +4990,19 @@ async function install(client, opts) {
|
|
|
4947
4990
|
}
|
|
4948
4991
|
|
|
4949
4992
|
// src/commands/doctor.ts
|
|
4950
|
-
var
|
|
4951
|
-
var
|
|
4993
|
+
var fs8 = __toESM(require("fs"));
|
|
4994
|
+
var path8 = __toESM(require("path"));
|
|
4952
4995
|
var os3 = __toESM(require("os"));
|
|
4953
4996
|
function readText(p) {
|
|
4954
4997
|
try {
|
|
4955
|
-
return
|
|
4998
|
+
return fs8.readFileSync(p, "utf-8");
|
|
4956
4999
|
} catch {
|
|
4957
5000
|
return "";
|
|
4958
5001
|
}
|
|
4959
5002
|
}
|
|
4960
5003
|
function fileExists(p) {
|
|
4961
5004
|
try {
|
|
4962
|
-
return
|
|
5005
|
+
return fs8.existsSync(p);
|
|
4963
5006
|
} catch {
|
|
4964
5007
|
return false;
|
|
4965
5008
|
}
|
|
@@ -4982,9 +5025,9 @@ function badge(s) {
|
|
|
4982
5025
|
async function doctor(client) {
|
|
4983
5026
|
const codexHome = resolveCodexHome();
|
|
4984
5027
|
const checks = [];
|
|
4985
|
-
const claudeSkill =
|
|
4986
|
-
const claudeSettings =
|
|
4987
|
-
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");
|
|
4988
5031
|
checks.push({
|
|
4989
5032
|
host: "claude",
|
|
4990
5033
|
skill: fileExists(claudeSkill) ? "ok" : "missing",
|
|
@@ -4992,8 +5035,8 @@ async function doctor(client) {
|
|
|
4992
5035
|
mcpConfig: mcpServersExomind(claudeJson, "mcpServers") ? "ok" : "missing",
|
|
4993
5036
|
paths: [claudeSkill, claudeSettings, claudeJson]
|
|
4994
5037
|
});
|
|
4995
|
-
const codexSkill =
|
|
4996
|
-
const codexConfig =
|
|
5038
|
+
const codexSkill = path8.join(codexHome, "skills", "exomind", "SKILL.md");
|
|
5039
|
+
const codexConfig = path8.join(codexHome, "config.toml");
|
|
4997
5040
|
checks.push({
|
|
4998
5041
|
host: "codex",
|
|
4999
5042
|
skill: fileExists(codexSkill) ? "ok" : "missing",
|
|
@@ -5002,7 +5045,7 @@ async function doctor(client) {
|
|
|
5002
5045
|
paths: [codexSkill, codexConfig],
|
|
5003
5046
|
detail: "Codex \u65E0 prompt-submit hook,\u9760 skill \u89E6\u53D1(jdit/\u5B58\u6863/\u67E5\u8BE2)"
|
|
5004
5047
|
});
|
|
5005
|
-
const ocJson =
|
|
5048
|
+
const ocJson = path8.join(os3.homedir(), ".config", "opencode", "opencode.json");
|
|
5006
5049
|
checks.push({
|
|
5007
5050
|
host: "opencode",
|
|
5008
5051
|
skill: "unsupported",
|
|
@@ -5080,6 +5123,7 @@ function run(fn) {
|
|
|
5080
5123
|
const client = new ApiClient(cfg);
|
|
5081
5124
|
try {
|
|
5082
5125
|
await fn(client, command.opts(), command.args);
|
|
5126
|
+
if (!root.json) await maybeNotifyUpdate(VERSION);
|
|
5083
5127
|
} catch (e) {
|
|
5084
5128
|
handleError(e);
|
|
5085
5129
|
}
|