exomind 0.6.0 → 0.7.1
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 +123 -40
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/skill/claude/SKILL.md +1 -0
package/dist/cli.js
CHANGED
|
@@ -3119,7 +3119,7 @@ var {
|
|
|
3119
3119
|
// package.json
|
|
3120
3120
|
var package_default = {
|
|
3121
3121
|
name: "exomind",
|
|
3122
|
-
version: "0.
|
|
3122
|
+
version: "0.7.1",
|
|
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"
|
|
@@ -3917,31 +3917,69 @@ async function mapWithConcurrency(items, concurrency, worker) {
|
|
|
3917
3917
|
}
|
|
3918
3918
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3919
3919
|
var suspended = false;
|
|
3920
|
-
|
|
3920
|
+
function friendlyMsg(e) {
|
|
3921
|
+
if (e instanceof ApiError) {
|
|
3922
|
+
if (e.status === 504) return "\u670D\u52A1\u5668\u5904\u7406\u8D85\u65F6";
|
|
3923
|
+
if (e.status === 502) return "\u7F51\u5173\u5F02\u5E38";
|
|
3924
|
+
if (e.status === 503) return "\u670D\u52A1\u6682\u4E0D\u53EF\u7528";
|
|
3925
|
+
if (e.status === 0) return "\u7F51\u7EDC\u9519\u8BEF";
|
|
3926
|
+
}
|
|
3927
|
+
return (e.message || String(e)).slice(0, 200);
|
|
3928
|
+
}
|
|
3929
|
+
async function ingestWithRetry(client, payload, timeoutMs, url = "/ingest") {
|
|
3921
3930
|
let rateAttempts = 0;
|
|
3922
3931
|
let quotaWaits = 0;
|
|
3932
|
+
let transientAttempts = 0;
|
|
3923
3933
|
while (true) {
|
|
3924
3934
|
try {
|
|
3925
|
-
return await client.post(
|
|
3935
|
+
return await client.post(url, payload, { timeoutMs });
|
|
3926
3936
|
} catch (e) {
|
|
3927
|
-
if (!(e instanceof ApiError)
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
if (
|
|
3931
|
-
|
|
3932
|
-
|
|
3937
|
+
if (!(e instanceof ApiError)) throw e;
|
|
3938
|
+
if (e.status === 429) {
|
|
3939
|
+
const type = e.body?.type;
|
|
3940
|
+
if (type === "daily_quota") {
|
|
3941
|
+
if (++quotaWaits > 3) throw new ApiError(429, "\u914D\u989D\u8FDE\u7EED 3 \u4E2A\u81EA\u7136\u65E5\u672A\u6062\u590D,\u653E\u5F03");
|
|
3942
|
+
await suspendUntilMidnight(Number(e.body?.reset ?? 0));
|
|
3943
|
+
continue;
|
|
3944
|
+
}
|
|
3945
|
+
if (type === "rate_limit") {
|
|
3946
|
+
if (++rateAttempts >= 5) throw new ApiError(429, "\u5E76\u53D1\u9650\u6D41,\u91CD\u8BD5 5 \u6B21\u4ECD\u5931\u8D25");
|
|
3947
|
+
const retry = Number(e.headers["retry-after"] ?? e.body?.retry_after ?? 5);
|
|
3948
|
+
process.stderr.write(dim(` \u23F8 \u5E76\u53D1\u9650\u6D41,${retry}s \u540E\u91CD\u8BD5
|
|
3949
|
+
`));
|
|
3950
|
+
await sleep(retry * 1e3);
|
|
3951
|
+
continue;
|
|
3952
|
+
}
|
|
3953
|
+
throw e;
|
|
3933
3954
|
}
|
|
3934
|
-
if (
|
|
3935
|
-
if (++
|
|
3936
|
-
const
|
|
3937
|
-
process.stderr.write(dim(` \u23F8
|
|
3955
|
+
if ([502, 503, 504].includes(e.status) || e.status === 0) {
|
|
3956
|
+
if (++transientAttempts > 3) throw e;
|
|
3957
|
+
const backoff = Math.pow(2, transientAttempts - 1) * 1e3;
|
|
3958
|
+
process.stderr.write(dim(` \u23F8 ${friendlyMsg(e)},${backoff / 1e3}s \u540E\u91CD\u8BD5 (${transientAttempts}/3)
|
|
3938
3959
|
`));
|
|
3939
|
-
await sleep(
|
|
3960
|
+
await sleep(backoff);
|
|
3961
|
+
continue;
|
|
3962
|
+
}
|
|
3963
|
+
throw e;
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
async function retryTransient(fn, maxAttempts = 3) {
|
|
3968
|
+
let lastErr;
|
|
3969
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
3970
|
+
try {
|
|
3971
|
+
return await fn();
|
|
3972
|
+
} catch (e) {
|
|
3973
|
+
lastErr = e;
|
|
3974
|
+
const transient = e instanceof ApiError && ([502, 503, 504].includes(e.status) || e.status === 0);
|
|
3975
|
+
if (transient && attempt < maxAttempts) {
|
|
3976
|
+
await sleep(Math.pow(2, attempt - 1) * 1e3);
|
|
3940
3977
|
continue;
|
|
3941
3978
|
}
|
|
3942
3979
|
throw e;
|
|
3943
3980
|
}
|
|
3944
3981
|
}
|
|
3982
|
+
throw lastErr;
|
|
3945
3983
|
}
|
|
3946
3984
|
async function suspendUntilMidnight(resetEpoch) {
|
|
3947
3985
|
suspended = true;
|
|
@@ -3975,7 +4013,7 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
3975
4013
|
const total = files.length;
|
|
3976
4014
|
const concurrency = Math.max(1, opts.concurrency ?? 3);
|
|
3977
4015
|
process.stderr.write(
|
|
3978
|
-
`\u76EE\u5F55 ${dir}: ${total} \u6587\u4EF6 \u2014 \u5F85\u6444\u5165 ${plan.toIngest.length},\u8DF3\u8FC7 ${plan.toSkip.length}(\u5E76\u53D1 ${concurrency})
|
|
4016
|
+
`\u76EE\u5F55 ${dir}: ${total} \u6587\u4EF6 \u2014 \u5F85\u6444\u5165 ${plan.toIngest.length},\u8DF3\u8FC7 ${plan.toSkip.length}(\u5F02\u6B65,\u5E76\u53D1\u63D0\u4EA4 ${concurrency})
|
|
3979
4017
|
`
|
|
3980
4018
|
);
|
|
3981
4019
|
const onSigInt = () => {
|
|
@@ -3989,56 +4027,101 @@ async function runDirIngestest(client, opts, dir) {
|
|
|
3989
4027
|
let added = 0;
|
|
3990
4028
|
let updated = 0;
|
|
3991
4029
|
let failed = 0;
|
|
3992
|
-
let
|
|
3993
|
-
const
|
|
4030
|
+
let degraded = 0;
|
|
4031
|
+
const pending = [];
|
|
3994
4032
|
try {
|
|
3995
|
-
await mapWithConcurrency(
|
|
3996
|
-
const prev = manifest[f.path];
|
|
3997
|
-
const seq = ++started;
|
|
3998
|
-
process.stderr.write(`\u23F3 [${seq}/${queue.length}] ${path4.basename(f.path)}\u2026
|
|
3999
|
-
`);
|
|
4033
|
+
await mapWithConcurrency(plan.toIngest, concurrency, async (f) => {
|
|
4000
4034
|
try {
|
|
4001
4035
|
const res = await ingestWithRetry(
|
|
4002
4036
|
client,
|
|
4003
4037
|
{ content: f.content, title: f.title, tags: opts.tag },
|
|
4004
|
-
|
|
4038
|
+
3e4,
|
|
4039
|
+
"/ingest/async"
|
|
4005
4040
|
);
|
|
4006
|
-
|
|
4007
|
-
saveManifest(manifest);
|
|
4008
|
-
if (prev) {
|
|
4009
|
-
updated++;
|
|
4010
|
-
process.stderr.write(` ${green("\u2713")} \u66F4\u65B0 \u2014 \u5B9E\u4F53 ${res.entities ?? 0}/\u6982\u5FF5 ${res.concepts ?? 0}
|
|
4011
|
-
`);
|
|
4012
|
-
} else {
|
|
4013
|
-
added++;
|
|
4014
|
-
process.stderr.write(` ${green("\u2713")} \u65B0\u589E \u2014 \u5B9E\u4F53 ${res.entities ?? 0}/\u6982\u5FF5 ${res.concepts ?? 0}
|
|
4015
|
-
`);
|
|
4016
|
-
}
|
|
4041
|
+
pending.push({ file: f, jobId: res.job_id });
|
|
4017
4042
|
} catch (e) {
|
|
4018
4043
|
failed++;
|
|
4019
|
-
process.stderr.write(` ${red("\u2717")} ${
|
|
4044
|
+
process.stderr.write(` ${red("\u2717")} ${path4.basename(f.path)} \u63D0\u4EA4\u5931\u8D25 \u2014 ${friendlyMsg(e)}
|
|
4020
4045
|
`);
|
|
4021
4046
|
}
|
|
4022
4047
|
});
|
|
4048
|
+
const totalJobs = pending.length;
|
|
4049
|
+
let done = 0;
|
|
4050
|
+
const pollDeadline = Date.now() + opTimeout(6e5);
|
|
4051
|
+
const inFlight = new Map(
|
|
4052
|
+
pending.map((p) => [p.jobId, p.file])
|
|
4053
|
+
);
|
|
4054
|
+
while (inFlight.size > 0) {
|
|
4055
|
+
if (Date.now() > pollDeadline) {
|
|
4056
|
+
for (const [, f] of inFlight) {
|
|
4057
|
+
failed++;
|
|
4058
|
+
process.stderr.write(` ${red("\u2717")} ${path4.basename(f.path)} \u8F6E\u8BE2\u8D85\u65F6
|
|
4059
|
+
`);
|
|
4060
|
+
}
|
|
4061
|
+
inFlight.clear();
|
|
4062
|
+
break;
|
|
4063
|
+
}
|
|
4064
|
+
await Promise.all(
|
|
4065
|
+
[...inFlight.entries()].map(async ([jobId, f]) => {
|
|
4066
|
+
try {
|
|
4067
|
+
const s = await retryTransient(() => client.get("/ingest/status", { job_id: jobId }));
|
|
4068
|
+
if (s.status !== "done" && s.status !== "failed") return;
|
|
4069
|
+
inFlight.delete(jobId);
|
|
4070
|
+
done++;
|
|
4071
|
+
if (s.status === "failed") {
|
|
4072
|
+
failed++;
|
|
4073
|
+
process.stderr.write(` ${red("\u2717")} ${path4.basename(f.path)} \u5904\u7406\u5931\u8D25 \u2014 ${s.error || "\u672A\u77E5\u9519\u8BEF"}
|
|
4074
|
+
`);
|
|
4075
|
+
return;
|
|
4076
|
+
}
|
|
4077
|
+
const prev = manifest[f.path];
|
|
4078
|
+
recordFile(manifest, f.path, f.content, f.title);
|
|
4079
|
+
saveManifest(manifest);
|
|
4080
|
+
if (s.extracted === false) {
|
|
4081
|
+
degraded++;
|
|
4082
|
+
process.stderr.write(` \u26A0 ${path4.basename(f.path)} \u964D\u7EA7 \u2014 \u539F\u6587\u5DF2\u5B58,\u5B9E\u4F53\u62BD\u53D6\u5F85\u8865\u8DD1
|
|
4083
|
+
`);
|
|
4084
|
+
} else if (prev) {
|
|
4085
|
+
updated++;
|
|
4086
|
+
process.stderr.write(` ${green("\u2713")} \u66F4\u65B0 \u2014 \u5B9E\u4F53 ${s.entities ?? 0}/\u6982\u5FF5 ${s.concepts ?? 0}
|
|
4087
|
+
`);
|
|
4088
|
+
} else {
|
|
4089
|
+
added++;
|
|
4090
|
+
process.stderr.write(` ${green("\u2713")} \u65B0\u589E \u2014 \u5B9E\u4F53 ${s.entities ?? 0}/\u6982\u5FF5 ${s.concepts ?? 0}
|
|
4091
|
+
`);
|
|
4092
|
+
}
|
|
4093
|
+
} catch {
|
|
4094
|
+
}
|
|
4095
|
+
})
|
|
4096
|
+
);
|
|
4097
|
+
if (inFlight.size > 0) {
|
|
4098
|
+
process.stderr.write(`${dim("\u23F3")} [\u5B8C\u6210 ${done}/${totalJobs}] \u8F6E\u8BE2\u4E2D\u2026
|
|
4099
|
+
`);
|
|
4100
|
+
await sleep(2e3);
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4023
4103
|
cleanupStale(manifest, dir, files);
|
|
4024
4104
|
saveManifest(manifest);
|
|
4025
4105
|
} finally {
|
|
4026
4106
|
process.removeListener("SIGINT", onSigInt);
|
|
4027
4107
|
}
|
|
4028
|
-
const allUpToDate = added + updated === 0 && plan.toSkip.length > 0 && failed === 0;
|
|
4108
|
+
const allUpToDate = added + updated + degraded === 0 && plan.toSkip.length > 0 && failed === 0;
|
|
4029
4109
|
output(
|
|
4030
|
-
{ added, updated, skipped: plan.toSkip.length, failed, total, dir, allUpToDate },
|
|
4110
|
+
{ added, updated, degraded, skipped: plan.toSkip.length, failed, total, dir, allUpToDate },
|
|
4031
4111
|
() => {
|
|
4032
4112
|
console.log(
|
|
4033
4113
|
green("\u2713 \u76EE\u5F55\u6444\u5165\u5B8C\u6210") + dim(
|
|
4034
|
-
`: \u65B0\u589E ${added} / \u66F4\u65B0 ${updated} / \u8DF3\u8FC7 ${plan.toSkip.length} / \u5931\u8D25 ${failed} (\u5171 ${total})`
|
|
4114
|
+
`: \u65B0\u589E ${added} / \u66F4\u65B0 ${updated} / \u964D\u7EA7 ${degraded} / \u8DF3\u8FC7 ${plan.toSkip.length} / \u5931\u8D25 ${failed} (\u5171 ${total})`
|
|
4035
4115
|
)
|
|
4036
4116
|
);
|
|
4037
|
-
if (
|
|
4117
|
+
if (degraded > 0) {
|
|
4038
4118
|
console.log(
|
|
4039
|
-
dim(
|
|
4119
|
+
dim(`\uFF08${degraded} \u6761\u964D\u7EA7:\u539F\u6587\u5DF2\u5165\u5E93,\u5B9E\u4F53\u62BD\u53D6\u5F85\u8865\u2014\u2014\u53EF\u7A0D\u540E\u91CD\u8DD1\u6216 exomind ingest --backfill \u8865\u8DD1\uFF09`)
|
|
4040
4120
|
);
|
|
4041
4121
|
}
|
|
4122
|
+
if (allUpToDate) {
|
|
4123
|
+
console.log(dim("\uFF08\u5168\u90E8\u5DF2\u662F\u6700\u65B0,\u65E0\u9700\u91CD\u6444;\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u5F3A\u5236\u5237\u65B0,\u5426\u5219\u4E0D\u8981\u52A0 --force\uFF09"));
|
|
4124
|
+
}
|
|
4042
4125
|
}
|
|
4043
4126
|
);
|
|
4044
4127
|
}
|