exomind 0.18.1 → 0.18.2

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 CHANGED
@@ -3119,7 +3119,7 @@ var {
3119
3119
  // package.json
3120
3120
  var package_default = {
3121
3121
  name: "exomind",
3122
- version: "0.18.1",
3122
+ version: "0.18.2",
3123
3123
  description: "ExoMind \u8DE8\u5E73\u53F0\u547D\u4EE4\u884C\u5BA2\u6237\u7AEF \u2014 \u901A\u8FC7 REST \u4E0E ExoMind \u77E5\u8BC6\u98DE\u8F6E\u4EA4\u4E92(ingest/query/search/review),\u66FF\u4EE3 Windows \u4E0D\u53EF\u7528\u7684 MCP \u5BA2\u6237\u7AEF\u3002",
3124
3124
  bin: {
3125
3125
  exomind: "dist/cli.js",
@@ -3326,8 +3326,8 @@ function setJsonMode(v) {
3326
3326
  function isJsonMode() {
3327
3327
  return JSON_MODE;
3328
3328
  }
3329
- function hint(msg) {
3330
- if (!isJsonMode()) process.stderr.write(msg + "\n");
3329
+ function hint(msg3) {
3330
+ if (!isJsonMode()) process.stderr.write(msg3 + "\n");
3331
3331
  }
3332
3332
  function output(data, pretty) {
3333
3333
  if (JSON_MODE) {
@@ -3347,6 +3347,9 @@ var gray = import_picocolors.default.gray;
3347
3347
  function ok(label) {
3348
3348
  return import_picocolors.default.green(`\u2713 ${label}`);
3349
3349
  }
3350
+ function fail(label) {
3351
+ return import_picocolors.default.red(`\u2717 ${label}`);
3352
+ }
3350
3353
  function truncate(s, n) {
3351
3354
  s = (s ?? "").replace(/\s+/g, " ").trim();
3352
3355
  return s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
@@ -3542,29 +3545,29 @@ function saveDedup(d) {
3542
3545
  } catch {
3543
3546
  }
3544
3547
  }
3545
- function hasTechTerm(msg) {
3546
- return /`|```|\w+\.\w{1,5}\b|[A-Z][a-z]+[A-Z]|npm |pip |git |docker|api|http/i.test(msg);
3548
+ function hasTechTerm(msg3) {
3549
+ return /`|```|\w+\.\w{1,5}\b|[A-Z][a-z]+[A-Z]|npm |pip |git |docker|api|http/i.test(msg3);
3547
3550
  }
3548
- function isSecretWord(msg) {
3549
- const m = msg.trim();
3551
+ function isSecretWord(msg3) {
3552
+ const m = msg3.trim();
3550
3553
  return /^(存档|jdit)[\s!.,;:!?。?、~…]*$/i.test(m);
3551
3554
  }
3552
- function matchesExperience(msg) {
3553
- if (msg.length < 50) return false;
3555
+ function matchesExperience(msg3) {
3556
+ if (msg3.length < 50) return false;
3554
3557
  let hits = 0;
3555
- for (const re of EXPERIENCE_PATTERNS) if (re.test(msg)) hits++;
3556
- const bullets = (msg.match(/^\s*([-*]|\d+[.、)])/gm) || []).length;
3557
- return hits >= 1 || bullets >= 3 && hasTechTerm(msg);
3558
+ for (const re of EXPERIENCE_PATTERNS) if (re.test(msg3)) hits++;
3559
+ const bullets = (msg3.match(/^\s*([-*]|\d+[.、)])/gm) || []).length;
3560
+ return hits >= 1 || bullets >= 3 && hasTechTerm(msg3);
3558
3561
  }
3559
- function matchesResearch(msg) {
3560
- if (msg.length < 100) return false;
3562
+ function matchesResearch(msg3) {
3563
+ if (msg3.length < 100) return false;
3561
3564
  let score = 0;
3562
- for (const re of RESEARCH_PATTERNS) if (re.test(msg)) score++;
3565
+ for (const re of RESEARCH_PATTERNS) if (re.test(msg3)) score++;
3563
3566
  let phases = 0;
3564
3567
  for (const sigs of Object.values(STRUCTURE_SIGNALS)) {
3565
- if (sigs.some((r) => r.test(msg))) phases++;
3568
+ if (sigs.some((r) => r.test(msg3))) phases++;
3566
3569
  }
3567
- const hasStructure = /(^|\n)\s*(#{1,4}\s|[-*]\s|\d+[.、)])/m.test(msg);
3570
+ const hasStructure = /(^|\n)\s*(#{1,4}\s|[-*]\s|\d+[.、)])/m.test(msg3);
3568
3571
  return score >= 2 && phases >= 3 || score >= 3 && hasStructure && phases >= 2;
3569
3572
  }
3570
3573
  async function getKeywordIndex(client, deadline = Number.POSITIVE_INFINITY) {
@@ -3683,13 +3686,13 @@ ${desc}
3683
3686
  out += "\u4EE5\u4E0A " + ents.length + ' \u6761\u6765\u81EA\u4F60\u7684\u77E5\u8BC6\u98DE\u8F6E(\u5DF2\u6CE8\u5165\u4E0A\u4E0B\u6587)\u3002**\u9F13\u52B1\u4E3B\u52A8\u53CD\u67E5\u8BA9\u98DE\u8F6E\u8F6C\u8D77\u6765**:\u9047\u95EE\u9898\u5148 `exomind query "<\u95EE\u9898>"`(\u4E0D\u53EA"\u5B58",\u66F4\u8981"\u7528"\u2014\u2014\u53CD\u67E5\u662F\u98DE\u8F6E\u589E\u5F3A\u56DE\u8DEF\u7684\u5173\u952E),\u518D\u56DE\u7B54\u3002';
3684
3687
  return out;
3685
3688
  }
3686
- async function buildContext(client, msg, dedup, now, deadline = Number.POSITIVE_INFINITY) {
3689
+ async function buildContext(client, msg3, dedup, now, deadline = Number.POSITIVE_INFINITY) {
3687
3690
  const index = await getKeywordIndex(client, deadline);
3688
3691
  if (!index) {
3689
3692
  return '[ExoMind] \u77E5\u8BC6\u98DE\u8F6E\u4E0A\u4E0B\u6587\u672C\u6B21\u672A\u6CE8\u5165(\u5173\u952E\u8BCD\u8868\u62C9\u53D6\u8D85\u65F6\u6216\u5931\u8D25);\u9700\u8981\u53CD\u67E5\u53EF\u8FD0\u884C `exomind search "\u5173\u952E\u8BCD"`\u3002';
3690
3693
  }
3691
3694
  const candidates = [...index.names || [], ...index.aliases || []];
3692
- const matched = matchEntities(msg, candidates);
3695
+ const matched = matchEntities(msg3, candidates);
3693
3696
  if (!matched.length) return "";
3694
3697
  const picked = [];
3695
3698
  const pickedCanonical = /* @__PURE__ */ new Set();
@@ -3724,23 +3727,23 @@ async function runHook(client) {
3724
3727
  const deadline = Date.now() + hookDeadlineMs();
3725
3728
  const raw = await readStdin();
3726
3729
  if (!raw) return;
3727
- let msg = "";
3730
+ let msg3 = "";
3728
3731
  try {
3729
3732
  const j = JSON.parse(raw);
3730
- msg = (j.prompt || j.message || "").trim();
3733
+ msg3 = (j.prompt || j.message || "").trim();
3731
3734
  } catch {
3732
- msg = raw.trim();
3735
+ msg3 = raw.trim();
3733
3736
  }
3734
- if (!msg) return;
3737
+ if (!msg3) return;
3735
3738
  const outputs = [];
3736
3739
  const dedup = loadDedup();
3737
3740
  const now = Date.now();
3738
- const isSecret = isSecretWord(msg);
3741
+ const isSecret = isSecretWord(msg3);
3739
3742
  if (!isSecret) {
3740
- if (msg.length < 8) return;
3741
- if (CONFIRM_WORDS.has(msg.toLowerCase())) return;
3743
+ if (msg3.length < 8) return;
3744
+ if (CONFIRM_WORDS.has(msg3.toLowerCase())) return;
3742
3745
  }
3743
- if (isSecret && msg.length < 20) {
3746
+ if (isSecret && msg3.length < 20) {
3744
3747
  if (now - dedup.lastArchive > COOLDOWN_MS) {
3745
3748
  outputs.push(ARCHIVE_INSTRUCTION);
3746
3749
  dedup.lastArchive = now;
@@ -3751,11 +3754,11 @@ async function runHook(client) {
3751
3754
  );
3752
3755
  }
3753
3756
  } else if (!isSecret) {
3754
- if (matchesExperience(msg)) outputs.push(EXPERIENCE_INSTRUCTION);
3755
- else if (matchesResearch(msg)) outputs.push(RESEARCH_INSTRUCTION);
3757
+ if (matchesExperience(msg3)) outputs.push(EXPERIENCE_INSTRUCTION);
3758
+ else if (matchesResearch(msg3)) outputs.push(RESEARCH_INSTRUCTION);
3756
3759
  }
3757
3760
  try {
3758
- const ctx = await buildContext(client, msg, dedup, now, deadline);
3761
+ const ctx = await buildContext(client, msg3, dedup, now, deadline);
3759
3762
  if (ctx) outputs.push(ctx);
3760
3763
  } catch {
3761
3764
  }
@@ -3856,9 +3859,9 @@ async function handleTool(client, name, args) {
3856
3859
  throw new Error(`\u672A\u77E5\u5DE5\u5177: ${name}`);
3857
3860
  }
3858
3861
  }
3859
- async function handleMessage(client, msg) {
3860
- const id = msg.id;
3861
- const method = msg.method;
3862
+ async function handleMessage(client, msg3) {
3863
+ const id = msg3.id;
3864
+ const method = msg3.method;
3862
3865
  if (method === "initialize") {
3863
3866
  return {
3864
3867
  jsonrpc: "2.0",
@@ -3874,7 +3877,7 @@ async function handleMessage(client, msg) {
3874
3877
  if (method === "ping") return { jsonrpc: "2.0", id, result: {} };
3875
3878
  if (method === "tools/list") return { jsonrpc: "2.0", id, result: { tools: TOOLS } };
3876
3879
  if (method === "tools/call") {
3877
- const params = msg.params ?? {};
3880
+ const params = msg3.params ?? {};
3878
3881
  try {
3879
3882
  const data = await handleTool(client, params.name ?? "", params.arguments ?? {});
3880
3883
  return {
@@ -3900,20 +3903,20 @@ async function runMcpServer(client) {
3900
3903
  rl.on("line", async (line) => {
3901
3904
  const trimmed = line.trim();
3902
3905
  if (!trimmed) return;
3903
- let msg;
3906
+ let msg3;
3904
3907
  try {
3905
- msg = JSON.parse(trimmed);
3908
+ msg3 = JSON.parse(trimmed);
3906
3909
  } catch {
3907
3910
  return;
3908
3911
  }
3909
3912
  try {
3910
- const resp = await handleMessage(client, msg);
3913
+ const resp = await handleMessage(client, msg3);
3911
3914
  if (resp) process.stdout.write(JSON.stringify(resp) + "\n");
3912
3915
  } catch (e) {
3913
3916
  process.stdout.write(
3914
3917
  JSON.stringify({
3915
3918
  jsonrpc: "2.0",
3916
- id: msg?.id ?? null,
3919
+ id: msg3?.id ?? null,
3917
3920
  error: { code: -32603, message: e instanceof Error ? e.message : String(e) }
3918
3921
  }) + "\n"
3919
3922
  );
@@ -4797,10 +4800,12 @@ async function whoami(client) {
4797
4800
 
4798
4801
  // src/commands/delete.ts
4799
4802
  var readline3 = __toESM(require("readline/promises"));
4803
+
4804
+ // src/page_path.ts
4800
4805
  function encPath(p) {
4801
4806
  return p.split("/").map(encodeURIComponent).join("/");
4802
4807
  }
4803
- async function resolvePage(client, page) {
4808
+ async function resolvePagePath(client, page) {
4804
4809
  const dirs = ["entities", "concepts", "summaries", "synthesis", "qa", "raw/articles"];
4805
4810
  for (const d of dirs) {
4806
4811
  const cand = `${d}/${page}.md`;
@@ -4810,76 +4815,159 @@ async function resolvePage(client, page) {
4810
4815
  } catch {
4811
4816
  }
4812
4817
  }
4813
- throw new Error(
4814
- `\u9875\u9762\u4E0D\u5B58\u5728: ${page}(\u5148 exomind search \u6216 exomind list \u5B9A\u4F4D;\u94FE\u63A5\u6846\u5165\u5E93\u7684\u539F\u6587\u5728 raw/articles/ \u4E0B)`
4815
- );
4818
+ throw new Error(`\u9875\u9762\u4E0D\u5B58\u5728: ${page}(\u5148 exomind search \u5B9A\u4F4D)`);
4816
4819
  }
4817
- async function deletePage(client, opts, args) {
4818
- const page = args.join(" ").trim();
4819
- if (!page) throw new Error("\u8BF7\u63D0\u4F9B\u9875\u9762\u8DEF\u5F84\u6216\u5B9E\u4F53\u540D: exomind delete entities/Redis.md");
4820
- let target = page;
4821
- if (!page.includes("/") && !page.endsWith(".md")) {
4822
- target = await resolvePage(client, page);
4823
- }
4824
- let title = "";
4820
+
4821
+ // src/commands/delete.ts
4822
+ function msg(e) {
4823
+ return e instanceof Error ? e.message : String(e);
4824
+ }
4825
+ async function toTarget(client, page) {
4826
+ return !page.includes("/") && !page.endsWith(".md") ? resolvePagePath(client, page) : page;
4827
+ }
4828
+ async function fetchTitle(client, target) {
4825
4829
  try {
4826
- const detail = await client.get(`/pages/${encPath(target)}`, void 0, {
4827
- timeoutMs: 15e3
4828
- });
4829
- title = String(detail?.title ?? "");
4830
+ const d = await client.get(`/pages/${encPath(target)}`, void 0, { timeoutMs: 15e3 });
4831
+ return String(d?.title ?? "");
4830
4832
  } catch {
4833
+ return "";
4831
4834
  }
4832
- if (!opts.yes && !isJsonMode()) {
4835
+ }
4836
+ async function deletePage(client, opts, args) {
4837
+ const pages = (args ?? []).map((a) => String(a).trim()).filter(Boolean);
4838
+ if (!pages.length) throw new Error("\u8BF7\u63D0\u4F9B\u9875\u9762\u8DEF\u5F84\u6216\u5B9E\u4F53\u540D: exomind delete entities/Redis.md");
4839
+ const plans = [];
4840
+ for (const p of pages) {
4841
+ try {
4842
+ const target = await toTarget(client, p);
4843
+ plans.push({ requested: p, target, title: await fetchTitle(client, target) });
4844
+ } catch (e) {
4845
+ plans.push({ requested: p, target: p, title: "", error: msg(e) });
4846
+ }
4847
+ }
4848
+ for (const p of plans) {
4849
+ if (p.error) hint(yellow(`\u26A0 ${p.requested} \u672A\u5B9A\u4F4D\u5230\u9875\u9762: ${p.error}`));
4850
+ }
4851
+ const doomed = plans.filter((p) => !p.error);
4852
+ if (!opts.yes && !isJsonMode() && doomed.length) {
4853
+ const head = doomed.length === 1 ? `\u5220\u9664 ${doomed[0].target}${doomed[0].title ? `\uFF08${doomed[0].title}\uFF09` : ""}\uFF1F` : `\u5220\u9664\u4EE5\u4E0B ${doomed.length} \u9875\uFF1F
4854
+ ${doomed.map((p) => ` ${p.target}${p.title ? `\uFF08${p.title}\uFF09` : ""}`).join("\n")}
4855
+ `;
4833
4856
  const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
4834
- const ans = await rl.question(
4835
- `\u5220\u9664 ${target}${title ? `\uFF08${title}\uFF09` : ""}\uFF1F\u5165\u670D\u52A1\u7AEF\u56DE\u6536\u7AD9\u3001\u53EF\u6062\u590D [y/N] `
4836
- );
4857
+ const ans = await rl.question(`${head}\u5165\u670D\u52A1\u7AEF\u56DE\u6536\u7AD9\u3001\u53EF\u6062\u590D [y/N] `);
4837
4858
  rl.close();
4838
4859
  if (ans.trim().toLowerCase() !== "y") {
4839
4860
  console.log("\u5DF2\u53D6\u6D88");
4840
4861
  return;
4841
4862
  }
4842
4863
  }
4843
- const r = await client.del(`/pages/${encPath(target)}`, { timeoutMs: 6e4 });
4844
- output(r, () => {
4845
- console.log(ok(`\u5DF2\u5220\u9664: ${r.path}`));
4846
- if (r.trash_path) console.log(dim(`\u56DE\u6536\u7AD9: ${r.trash_path} \u2014 exomind trash \u67E5\u770B / trash restore \u6062\u590D`));
4847
- });
4848
- }
4849
-
4850
- // src/page_path.ts
4851
- function encPath2(p) {
4852
- return p.split("/").map(encodeURIComponent).join("/");
4853
- }
4854
- async function resolvePagePath(client, page) {
4855
- const dirs = ["entities", "concepts", "summaries", "synthesis", "qa", "raw/articles"];
4856
- for (const d of dirs) {
4857
- const cand = `${d}/${page}.md`;
4864
+ const done = [];
4865
+ for (const p of plans) {
4866
+ if (p.error) {
4867
+ done.push(p);
4868
+ continue;
4869
+ }
4858
4870
  try {
4859
- await client.get(`/pages/${encPath2(cand)}`, void 0, { timeoutMs: 15e3 });
4860
- return cand;
4861
- } catch {
4871
+ const r = await client.del(`/pages/${encPath(p.target)}`, { timeoutMs: 6e4 });
4872
+ done.push({ ...p, path: r?.path, trash_path: r?.trash_path, raw: r });
4873
+ } catch (e) {
4874
+ done.push({ ...p, error: msg(e) });
4862
4875
  }
4863
4876
  }
4864
- throw new Error(`\u9875\u9762\u4E0D\u5B58\u5728: ${page}(\u5148 exomind search \u5B9A\u4F4D)`);
4877
+ const failed = done.filter((d) => d.error);
4878
+ const succeeded = done.filter((d) => !d.error);
4879
+ const mismatch = (d) => d.path && d.path !== d.target ? yellow(` \u26A0 \u5B9E\u9645\u5220\u9664\u7684\u662F ${d.path}\uFF08\u8BF7\u6C42\u7684\u662F ${d.target}\uFF09`) : null;
4880
+ if (done.length === 1) {
4881
+ const d = done[0];
4882
+ if (d.error) {
4883
+ output({ deleted: false, path: d.target, error: d.error }, () => {
4884
+ console.log(fail(`${d.requested} \u672A\u5220\u9664: ${d.error}`));
4885
+ });
4886
+ } else {
4887
+ output(d.raw ?? { deleted: true, path: d.path, trash_path: d.trash_path }, () => {
4888
+ console.log(ok(`\u5DF2\u5220\u9664: ${d.path}`));
4889
+ if (d.trash_path) {
4890
+ console.log(dim(`\u56DE\u6536\u7AD9: ${d.trash_path} \u2014 exomind trash \u67E5\u770B / trash restore \u6062\u590D`));
4891
+ }
4892
+ const m = mismatch(d);
4893
+ if (m) console.log(m);
4894
+ });
4895
+ }
4896
+ } else {
4897
+ output(
4898
+ {
4899
+ ok: failed.length === 0,
4900
+ total: done.length,
4901
+ deleted: succeeded.map((d) => ({ requested: d.requested, path: d.path, trash_path: d.trash_path })),
4902
+ failed: failed.map((d) => ({ requested: d.requested, target: d.target, error: d.error }))
4903
+ },
4904
+ () => {
4905
+ for (const d of done) {
4906
+ if (d.error) {
4907
+ console.log(fail(`${d.requested} \u672A\u5220\u9664: ${d.error}`));
4908
+ continue;
4909
+ }
4910
+ console.log(ok(`\u5DF2\u5220\u9664: ${d.path}`));
4911
+ const m = mismatch(d);
4912
+ if (m) console.log(m);
4913
+ if (d.trash_path) console.log(dim(` \u56DE\u6536\u7AD9: ${d.trash_path}`));
4914
+ }
4915
+ console.log(
4916
+ dim(
4917
+ `\u5171 ${done.length} \u9875: ${succeeded.length} \u6210\u529F${failed.length ? `, ${failed.length} \u5931\u8D25` : ""} \u2014 exomind trash restore <\u56DE\u6536\u7AD9\u8DEF\u5F84> \u6062\u590D`
4918
+ )
4919
+ );
4920
+ }
4921
+ );
4922
+ }
4923
+ if (failed.length) process.exitCode = 1;
4865
4924
  }
4866
4925
 
4867
4926
  // src/commands/get.ts
4927
+ function msg2(e) {
4928
+ return e instanceof Error ? e.message : String(e);
4929
+ }
4930
+ function render(r) {
4931
+ console.log(`${bold(r.title)} ${dim(`(${r.path})`)}`);
4932
+ if (r.tags?.length) console.log(dim(`\u6807\u7B7E: ${r.tags.join(", ")}`));
4933
+ console.log(dim(`\u66F4\u65B0: ${r.mtime ?? ""} \xB7 ${r.size_bytes ?? 0} \u5B57\u8282`));
4934
+ console.log("");
4935
+ console.log(truncate(String(r.body ?? ""), 2e3));
4936
+ }
4868
4937
  async function get(client, _opts, args) {
4869
- const page = args.join(" ").trim();
4870
- if (!page) throw new Error("\u8BF7\u63D0\u4F9B\u9875\u9762\u8DEF\u5F84\u6216\u5B9E\u4F53\u540D: exomind get entities/Redis.md");
4871
- let target = page;
4872
- if (!page.includes("/") && !page.endsWith(".md")) {
4873
- target = await resolvePagePath(client, page);
4938
+ const pages = (args ?? []).map((a) => String(a).trim()).filter(Boolean);
4939
+ if (!pages.length) throw new Error("\u8BF7\u63D0\u4F9B\u9875\u9762\u8DEF\u5F84\u6216\u5B9E\u4F53\u540D: exomind get entities/Redis.md");
4940
+ const results = [];
4941
+ let failed = 0;
4942
+ for (const page of pages) {
4943
+ try {
4944
+ const target = !page.includes("/") && !page.endsWith(".md") ? await resolvePagePath(client, page) : page;
4945
+ results.push(await client.get(`/pages/${encPath(target)}`, void 0, { timeoutMs: 3e4 }));
4946
+ } catch (e) {
4947
+ failed++;
4948
+ results.push({ error: msg2(e), requested: page });
4949
+ }
4874
4950
  }
4875
- const r = await client.get(`/pages/${encPath2(target)}`, void 0, { timeoutMs: 3e4 });
4876
- output(r, () => {
4877
- console.log(`${bold(r.title)} ${dim(`(${r.path})`)}`);
4878
- if (r.tags?.length) console.log(dim(`\u6807\u7B7E: ${r.tags.join(", ")}`));
4879
- console.log(dim(`\u66F4\u65B0: ${r.mtime ?? ""} \xB7 ${r.size_bytes ?? 0} \u5B57\u8282`));
4880
- console.log("");
4881
- console.log(truncate(String(r.body ?? ""), 2e3));
4882
- });
4951
+ if (pages.length === 1) {
4952
+ const r = results[0];
4953
+ if (r.error) {
4954
+ output({ error: r.error, requested: r.requested }, () => console.log(fail(`${r.requested}: ${r.error}`)));
4955
+ } else {
4956
+ output(r, () => render(r));
4957
+ }
4958
+ } else {
4959
+ output(results, () => {
4960
+ results.forEach((r, i) => {
4961
+ if (i) console.log("");
4962
+ if (r.error) {
4963
+ console.log(fail(`${r.requested}: ${r.error}`));
4964
+ return;
4965
+ }
4966
+ render(r);
4967
+ });
4968
+ });
4969
+ }
4970
+ if (failed) process.exitCode = 1;
4883
4971
  }
4884
4972
 
4885
4973
  // src/commands/trash.ts
@@ -4889,7 +4977,7 @@ async function trash(client, opts, args) {
4889
4977
  case "list":
4890
4978
  return doList2(client, opts);
4891
4979
  case "restore":
4892
- return doRestore(client, args[1]);
4980
+ return doRestore(client, args.slice(1));
4893
4981
  default:
4894
4982
  throw new Error(
4895
4983
  `\u672A\u77E5 trash \u5B50\u547D\u4EE4: ${action ?? "(\u7A7A)"}\u3002\u53EF\u7528: list \u5217\u8868 / restore <\u56DE\u6536\u7AD9\u8DEF\u5F84> \u6062\u590D`
@@ -4915,9 +5003,13 @@ async function doList2(client, opts) {
4915
5003
  );
4916
5004
  });
4917
5005
  }
4918
- async function doRestore(client, trashPath) {
4919
- const p = (trashPath ?? "").trim();
4920
- if (!p) throw new Error('\u8BF7\u63D0\u4F9B\u56DE\u6536\u7AD9\u8DEF\u5F84: exomind trash restore ".trash/202609/entities/Redis.md"');
5006
+ async function doRestore(client, targets) {
5007
+ const list2 = targets.map((t) => t.trim()).filter(Boolean);
5008
+ if (!list2.length) throw new Error('\u8BF7\u63D0\u4F9B\u56DE\u6536\u7AD9\u8DEF\u5F84: exomind trash restore ".trash/202609/entities/Redis.md"');
5009
+ if (list2.length > 1) {
5010
+ throw new Error(`\u4E00\u6B21\u53EA\u80FD\u6062\u590D\u4E00\u4E2A:\u6536\u5230 ${list2.length} \u4E2A\u8DEF\u5F84,\u8BF7\u9010\u4E2A\u6062\u590D(\u7B2C 1 \u4E2A: ${list2[0]})`);
5011
+ }
5012
+ const p = list2[0];
4921
5013
  console.log(dim("\u6062\u590D\u4E2D\u2026\u5927\u77E5\u8BC6\u98DE\u8F6E\u5237\u7D22\u5F15\u53EF\u80FD\u9700\u8981\u4E00\u4E24\u5206\u949F,\u8BF7\u52FF\u5173\u95ED"));
4922
5014
  const r = await client.post("/trash/restore", { trash_path: p }, { timeoutMs: opTimeout(3e5) });
4923
5015
  output(r, () => console.log(ok(`\u5DF2\u6062\u590D \u2192 ${r.path}`)));
@@ -5349,11 +5441,11 @@ function rootOptions(cmd) {
5349
5441
  return c.opts();
5350
5442
  }
5351
5443
  function handleError(e) {
5352
- const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : String(e);
5444
+ const msg3 = e instanceof ApiError ? e.message : e instanceof Error ? e.message : String(e);
5353
5445
  if (isJsonMode()) {
5354
- console.log(JSON.stringify({ error: msg }));
5446
+ console.log(JSON.stringify({ error: msg3 }));
5355
5447
  } else {
5356
- console.error(red(`\u2717 ${msg}`));
5448
+ console.error(red(`\u2717 ${msg3}`));
5357
5449
  }
5358
5450
  process.exit(1);
5359
5451
  }