dsh-vscode-mode 0.6.0 → 0.7.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/README.md +19 -2
- package/lib/client.js +8302 -7458
- package/lib/client.js.map +1 -1
- package/lib/index.js +373 -0
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/copyText.ts +28 -0
- package/src/client/fileClipboard.ts +46 -0
- package/src/client/index.ts +3 -3
- package/src/client/openWithDialog.ts +66 -0
- package/src/client/searchSeed.ts +36 -0
- package/src/client/sidebar/contextMenu.ts +51 -10
- package/src/client/sidebar/menuItems.ts +473 -50
- package/src/client/sidebar/panels/SearchPanel.ts +24 -3
- package/src/client/sidebar/types.ts +6 -0
- package/src/client/tabActions.ts +22 -10
- package/src/client/ui/EditorView.ts +81 -7
- package/src/client/ui/OfficialSideTab.ts +1 -0
- package/src/client/ui/PromptDialog.ts +63 -0
- package/src/client/ui/SideEditorTab.ts +1 -0
- package/src/client/ui/commandCatalog.ts +14 -0
- package/src/rpc.ts +198 -2
- package/src/shared/fsNames.ts +115 -0
- package/src/shared/keybindings.ts +2 -0
- package/src/shared/rpc.ts +12 -0
package/lib/index.js
CHANGED
|
@@ -130,6 +130,7 @@ const KEYBINDING_DEFAULTS = {
|
|
|
130
130
|
"edrv.navigateForward": "Alt+ArrowRight|Ctrl+Shift+-",
|
|
131
131
|
"edrv.nextTab": "Ctrl+Alt+ArrowRight|Ctrl+PageDown",
|
|
132
132
|
"edrv.prevTab": "Ctrl+Alt+ArrowLeft|Ctrl+PageUp",
|
|
133
|
+
"edrv.goToLine": "Ctrl+G",
|
|
133
134
|
"edrv.showCommands": "Ctrl+Shift+P|F1",
|
|
134
135
|
"edrv.nextEditorRow": "Ctrl+Alt+ArrowDown",
|
|
135
136
|
"edrv.prevEditorRow": "Ctrl+Alt+ArrowUp",
|
|
@@ -6670,6 +6671,102 @@ function pendingState(token, take) {
|
|
|
6670
6671
|
function clientsAlive() {
|
|
6671
6672
|
return Date.now() - lastPollAt <= PRESENCE_WINDOW_MS;
|
|
6672
6673
|
}
|
|
6674
|
+
/**
|
|
6675
|
+
* 路径归一:去首尾空白 + 反斜杠转 '/'(Windows 输入等价处理)。
|
|
6676
|
+
* @author ddj 2026年09月22号
|
|
6677
|
+
* @param raw 原始输入
|
|
6678
|
+
* @returns 归一后的字符串
|
|
6679
|
+
*/
|
|
6680
|
+
function normPath(raw) {
|
|
6681
|
+
return String(raw ?? "").trim().replace(/\\/g, "/");
|
|
6682
|
+
}
|
|
6683
|
+
/**
|
|
6684
|
+
* 单个名称段的通用违规检查(两套校验共用)。
|
|
6685
|
+
* @author ddj 2026年09月22号
|
|
6686
|
+
* @param seg 名称段
|
|
6687
|
+
* @returns 错误文案;null = 通过
|
|
6688
|
+
*/
|
|
6689
|
+
function checkSegment(seg) {
|
|
6690
|
+
if (!seg) return "名称包含空路径段";
|
|
6691
|
+
if (seg === "." || seg === "..") return "名称不能包含 . 或 .. 路径段";
|
|
6692
|
+
if (seg.length > 255) return "名称过长(单段超过 255 字符)";
|
|
6693
|
+
if (/[\0-\x1f]/.test(seg)) return "名称包含非法控制字符";
|
|
6694
|
+
return null;
|
|
6695
|
+
}
|
|
6696
|
+
/**
|
|
6697
|
+
* 校验新建文件/文件夹的相对路径(允许 a/b.c 嵌套段)。
|
|
6698
|
+
* @author ddj 2026年09月22号
|
|
6699
|
+
* @param raw 用户输入的名称或相对路径
|
|
6700
|
+
* @returns 错误文案;null = 校验通过
|
|
6701
|
+
*/
|
|
6702
|
+
function checkNewName(raw) {
|
|
6703
|
+
const text = normPath(raw);
|
|
6704
|
+
if (!text) return "名称不能为空";
|
|
6705
|
+
if (text.startsWith("/")) return "名称不能是绝对路径";
|
|
6706
|
+
if (/^[a-zA-Z]:/.test(text)) return "名称不能带盘符";
|
|
6707
|
+
for (const seg of text.split("/")) {
|
|
6708
|
+
const bad = checkSegment(seg);
|
|
6709
|
+
if (bad) return bad;
|
|
6710
|
+
}
|
|
6711
|
+
return null;
|
|
6712
|
+
}
|
|
6713
|
+
/**
|
|
6714
|
+
* 校验重命名的新名称(仅单个名称段,不接受路径)。
|
|
6715
|
+
* @author ddj 2026年09月22号
|
|
6716
|
+
* @param raw 用户输入的新名称
|
|
6717
|
+
* @returns 错误文案;null = 校验通过
|
|
6718
|
+
*/
|
|
6719
|
+
function checkRenameName(raw) {
|
|
6720
|
+
const text = normPath(raw);
|
|
6721
|
+
if (!text) return "名称不能为空";
|
|
6722
|
+
if (text.includes("/")) return "新名称不能包含路径分隔符";
|
|
6723
|
+
return checkSegment(text);
|
|
6724
|
+
}
|
|
6725
|
+
/**
|
|
6726
|
+
* 拼接工作区相对路径(dir 为空 = 根;name 新建时可含多段)。
|
|
6727
|
+
* @author ddj 2026年09月22号
|
|
6728
|
+
* @param dir 目标目录(工作区相对)
|
|
6729
|
+
* @param name 名称
|
|
6730
|
+
* @returns 拼接后的相对路径
|
|
6731
|
+
*/
|
|
6732
|
+
function joinRelPath(dir, name) {
|
|
6733
|
+
const base = normPath(dir).replace(/\/+$/, "");
|
|
6734
|
+
const add = normPath(name).replace(/^\/+/, "");
|
|
6735
|
+
return base ? base + "/" + add : add;
|
|
6736
|
+
}
|
|
6737
|
+
/**
|
|
6738
|
+
* 取相对路径的父目录(顶层条目返回空串 = 根)。
|
|
6739
|
+
* @author ddj 2026年09月22号
|
|
6740
|
+
* @param path 工作区相对路径
|
|
6741
|
+
* @returns 父目录相对路径
|
|
6742
|
+
*/
|
|
6743
|
+
function parentRelOf(path) {
|
|
6744
|
+
const parts = normPath(path).split("/").filter(Boolean);
|
|
6745
|
+
parts.pop();
|
|
6746
|
+
return parts.join("/");
|
|
6747
|
+
}
|
|
6748
|
+
/**
|
|
6749
|
+
* 取相对路径的名称段(末段)。
|
|
6750
|
+
* @author ddj 2026年09月22号
|
|
6751
|
+
* @param path 工作区相对路径
|
|
6752
|
+
* @returns 末段名称
|
|
6753
|
+
*/
|
|
6754
|
+
function baseNameOf(path) {
|
|
6755
|
+
return normPath(path).split("/").filter(Boolean).pop() ?? "";
|
|
6756
|
+
}
|
|
6757
|
+
/**
|
|
6758
|
+
* other 是否为 base 本身或其内部路径(复制/移动「不能以自身或其子目录为目标」护栏)。
|
|
6759
|
+
* @author ddj 2026年09月22号
|
|
6760
|
+
* @param base 基准相对路径(源)
|
|
6761
|
+
* @param other 待判定相对路径(目标目录)
|
|
6762
|
+
* @returns 是否为自身或子路径
|
|
6763
|
+
*/
|
|
6764
|
+
function isSubPath(base, other) {
|
|
6765
|
+
const b = normPath(base).replace(/\/+$/, "");
|
|
6766
|
+
const o = normPath(other).replace(/\/+$/, "");
|
|
6767
|
+
if (!b) return false;
|
|
6768
|
+
return o === b || o.startsWith(b + "/");
|
|
6769
|
+
}
|
|
6673
6770
|
//#endregion
|
|
6674
6771
|
//#region src/reveal.ts
|
|
6675
6772
|
/**
|
|
@@ -7539,6 +7636,103 @@ async function integrationBaseUrlOf(ctx) {
|
|
|
7539
7636
|
const value = (ctx.get("settings")?.describe?.({ redactSecrets: true })?.find((item) => item.ns === FILE_OPEN_SETTINGS_NS))?.value;
|
|
7540
7637
|
return (typeof value?.integrationBaseUrl === "string" ? value.integrationBaseUrl.trim() : "") || "http://127.0.0.1:3080";
|
|
7541
7638
|
}
|
|
7639
|
+
/**
|
|
7640
|
+
* 文件操作目标解析:工作区相对路径 → 绝对路径,并做工作区边界检查。
|
|
7641
|
+
* edrv.fs.* 各方法共用(对齐 edrv.saveBinary 的 contains 护栏口径)。
|
|
7642
|
+
* @author ddj 2026年09月22号
|
|
7643
|
+
* @param ctx DSH 上下文
|
|
7644
|
+
* @param session 当前会话
|
|
7645
|
+
* @param rel 工作区相对路径
|
|
7646
|
+
* @returns 绝对路径;缺 fs/越界/解析失败返回错误文案
|
|
7647
|
+
*/
|
|
7648
|
+
async function fsOpsTarget(ctx, session, rel) {
|
|
7649
|
+
const fs = ctx.get("fs");
|
|
7650
|
+
if (!fs) return { err: "缺少 fs" };
|
|
7651
|
+
try {
|
|
7652
|
+
const target = await resolveTarget(ctx, session, rel);
|
|
7653
|
+
const rootTarget = await fs.resolve(policyOf(ctx, session)?.workspaceRoot ?? ".", {});
|
|
7654
|
+
if (!fs.contains(rootTarget, target)) return { err: "拒绝操作:目标不在会话工作区内" };
|
|
7655
|
+
return { abs: fs.processPath(target) };
|
|
7656
|
+
} catch (error) {
|
|
7657
|
+
return { err: "路径解析失败:" + String(error) };
|
|
7658
|
+
}
|
|
7659
|
+
}
|
|
7660
|
+
/**
|
|
7661
|
+
* 取异常的系统错误码(EEXIST/EXDEV 等分支判定用)。
|
|
7662
|
+
* @author ddj 2026年09月22号
|
|
7663
|
+
* @param error 捕获到的异常
|
|
7664
|
+
* @returns 错误码;取不到返回空串
|
|
7665
|
+
*/
|
|
7666
|
+
function fsErrCode(error) {
|
|
7667
|
+
const code = error?.code;
|
|
7668
|
+
return typeof code === "string" ? code : "";
|
|
7669
|
+
}
|
|
7670
|
+
/**
|
|
7671
|
+
* 复制/移动共用核心(edrv.fsCopy / edrv.fsMove):目标目录存在 + 同名拒绝覆盖 +
|
|
7672
|
+
* 「不能以自身或其子目录为目标」护栏;move 跨设备(EXDEV)回退「递归复制 + 删除源」。
|
|
7673
|
+
* @author ddj 2026年09月22号
|
|
7674
|
+
* @param ctx DSH 上下文
|
|
7675
|
+
* @param sc requireSession 成功结果(session + cwd)
|
|
7676
|
+
* @param args 请求载荷(from / toDir 均为工作区相对路径)
|
|
7677
|
+
* @param mode copy = 复制;move = 移动
|
|
7678
|
+
* @returns 成功返回 from/to 相对路径;失败返回错误文案
|
|
7679
|
+
*/
|
|
7680
|
+
async function fsTransfer(ctx, sc, args, mode) {
|
|
7681
|
+
const fromRel = normalizeRel(args.from);
|
|
7682
|
+
const toDirRel = normalizeRel(args.toDir);
|
|
7683
|
+
if (fromRel === null || fromRel === "" || toDirRel === null) return { err: "路径不合法" };
|
|
7684
|
+
if (isSubPath(fromRel, toDirRel)) return { err: "不能以自身或其子目录为目标" };
|
|
7685
|
+
const src = await fsOpsTarget(ctx, sc.session, fromRel);
|
|
7686
|
+
if ("err" in src) return src;
|
|
7687
|
+
const dir = await fsOpsTarget(ctx, sc.session, toDirRel);
|
|
7688
|
+
if ("err" in dir) return dir;
|
|
7689
|
+
const dirInfo = await stat(dir.abs).catch(() => null);
|
|
7690
|
+
if (!dirInfo || !dirInfo.isDirectory()) return { err: "目标目录不存在" };
|
|
7691
|
+
const toRel = joinRelPath(toDirRel, baseNameOf(fromRel));
|
|
7692
|
+
const dst = await fsOpsTarget(ctx, sc.session, toRel);
|
|
7693
|
+
if ("err" in dst) return dst;
|
|
7694
|
+
if (!await stat(src.abs).catch(() => null)) return { err: "源不存在" };
|
|
7695
|
+
if (await stat(dst.abs).catch(() => null)) return { err: "目标目录已存在同名文件或目录" };
|
|
7696
|
+
try {
|
|
7697
|
+
await transferEntry(src.abs, dst.abs, mode);
|
|
7698
|
+
} catch (error) {
|
|
7699
|
+
return { err: (mode === "copy" ? "复制失败:" : "移动失败:") + String(error) };
|
|
7700
|
+
}
|
|
7701
|
+
invalidateIndex(ctx, sc.cwd, fromRel);
|
|
7702
|
+
invalidateIndex(ctx, sc.cwd, toRel);
|
|
7703
|
+
return {
|
|
7704
|
+
from: fromRel,
|
|
7705
|
+
to: toRel
|
|
7706
|
+
};
|
|
7707
|
+
}
|
|
7708
|
+
/**
|
|
7709
|
+
* 单条目复制/移动落盘(文件/目录通用;move 跨设备回退复制+删除源)。
|
|
7710
|
+
* @author ddj 2026年09月22号
|
|
7711
|
+
* @param absFrom 源绝对路径
|
|
7712
|
+
* @param absTo 目标绝对路径
|
|
7713
|
+
* @param mode copy = 复制;move = 移动
|
|
7714
|
+
*/
|
|
7715
|
+
async function transferEntry(absFrom, absTo, mode) {
|
|
7716
|
+
if (mode === "copy") {
|
|
7717
|
+
await cp(absFrom, absTo, {
|
|
7718
|
+
recursive: true,
|
|
7719
|
+
force: false,
|
|
7720
|
+
errorOnExist: true
|
|
7721
|
+
});
|
|
7722
|
+
return;
|
|
7723
|
+
}
|
|
7724
|
+
try {
|
|
7725
|
+
await rename(absFrom, absTo);
|
|
7726
|
+
} catch (error) {
|
|
7727
|
+
if (fsErrCode(error) !== "EXDEV") throw error;
|
|
7728
|
+
await cp(absFrom, absTo, {
|
|
7729
|
+
recursive: true,
|
|
7730
|
+
force: false,
|
|
7731
|
+
errorOnExist: true
|
|
7732
|
+
});
|
|
7733
|
+
await rm(absFrom, { recursive: true });
|
|
7734
|
+
}
|
|
7735
|
+
}
|
|
7542
7736
|
/** 各方法 handler 表(类型由 shared/rpc 的 RpcHandlerMap 约束)。 */
|
|
7543
7737
|
function buildHandlers(ctx, registry, searcher = newSearcher(ctx), contentSearcher = newContentSearcher(ctx), lspHandlers, aiHandlers, fileVersions, svnHandlers, dapHandlers) {
|
|
7544
7738
|
return {
|
|
@@ -8140,6 +8334,185 @@ function buildHandlers(ctx, registry, searcher = newSearcher(ctx), contentSearch
|
|
|
8140
8334
|
};
|
|
8141
8335
|
}
|
|
8142
8336
|
},
|
|
8337
|
+
"edrv.fsCreateFile": async (args) => {
|
|
8338
|
+
const sc = await requireSession(ctx, args.sessionId);
|
|
8339
|
+
if ("err" in sc) return {
|
|
8340
|
+
ok: false,
|
|
8341
|
+
error: sc.err
|
|
8342
|
+
};
|
|
8343
|
+
const bad = checkNewName(args.path);
|
|
8344
|
+
if (bad) return {
|
|
8345
|
+
ok: false,
|
|
8346
|
+
error: bad
|
|
8347
|
+
};
|
|
8348
|
+
const r = await fsOpsTarget(ctx, sc.session, args.path);
|
|
8349
|
+
if ("err" in r) return {
|
|
8350
|
+
ok: false,
|
|
8351
|
+
error: r.err
|
|
8352
|
+
};
|
|
8353
|
+
try {
|
|
8354
|
+
await mkdir(dirname(r.abs), { recursive: true });
|
|
8355
|
+
await writeFile(r.abs, "", { flag: "wx" });
|
|
8356
|
+
} catch (error) {
|
|
8357
|
+
return {
|
|
8358
|
+
ok: false,
|
|
8359
|
+
error: fsErrCode(error) === "EEXIST" ? "已存在同名文件或目录" : "新建文件失败:" + String(error)
|
|
8360
|
+
};
|
|
8361
|
+
}
|
|
8362
|
+
invalidateIndex(ctx, sc.cwd, args.path);
|
|
8363
|
+
return {
|
|
8364
|
+
ok: true,
|
|
8365
|
+
path: args.path
|
|
8366
|
+
};
|
|
8367
|
+
},
|
|
8368
|
+
"edrv.fsCreateDir": async (args) => {
|
|
8369
|
+
const sc = await requireSession(ctx, args.sessionId);
|
|
8370
|
+
if ("err" in sc) return {
|
|
8371
|
+
ok: false,
|
|
8372
|
+
error: sc.err
|
|
8373
|
+
};
|
|
8374
|
+
const bad = checkNewName(args.path);
|
|
8375
|
+
if (bad) return {
|
|
8376
|
+
ok: false,
|
|
8377
|
+
error: bad
|
|
8378
|
+
};
|
|
8379
|
+
const r = await fsOpsTarget(ctx, sc.session, args.path);
|
|
8380
|
+
if ("err" in r) return {
|
|
8381
|
+
ok: false,
|
|
8382
|
+
error: r.err
|
|
8383
|
+
};
|
|
8384
|
+
try {
|
|
8385
|
+
await mkdir(dirname(r.abs), { recursive: true });
|
|
8386
|
+
await mkdir(r.abs);
|
|
8387
|
+
} catch (error) {
|
|
8388
|
+
return {
|
|
8389
|
+
ok: false,
|
|
8390
|
+
error: fsErrCode(error) === "EEXIST" ? "已存在同名文件或目录" : "新建文件夹失败:" + String(error)
|
|
8391
|
+
};
|
|
8392
|
+
}
|
|
8393
|
+
invalidateIndex(ctx, sc.cwd, args.path);
|
|
8394
|
+
return {
|
|
8395
|
+
ok: true,
|
|
8396
|
+
path: args.path
|
|
8397
|
+
};
|
|
8398
|
+
},
|
|
8399
|
+
"edrv.fsRename": async (args) => {
|
|
8400
|
+
const sc = await requireSession(ctx, args.sessionId);
|
|
8401
|
+
if ("err" in sc) return {
|
|
8402
|
+
ok: false,
|
|
8403
|
+
error: sc.err
|
|
8404
|
+
};
|
|
8405
|
+
const bad = checkRenameName(args.newName);
|
|
8406
|
+
if (bad) return {
|
|
8407
|
+
ok: false,
|
|
8408
|
+
error: bad
|
|
8409
|
+
};
|
|
8410
|
+
const fromRel = normalizeRel(args.path);
|
|
8411
|
+
if (fromRel === null || fromRel === "") return {
|
|
8412
|
+
ok: false,
|
|
8413
|
+
error: "路径不合法"
|
|
8414
|
+
};
|
|
8415
|
+
const toRel = joinRelPath(parentRelOf(fromRel), args.newName);
|
|
8416
|
+
if (toRel === fromRel) return {
|
|
8417
|
+
ok: true,
|
|
8418
|
+
from: fromRel,
|
|
8419
|
+
to: fromRel
|
|
8420
|
+
};
|
|
8421
|
+
const src = await fsOpsTarget(ctx, sc.session, fromRel);
|
|
8422
|
+
if ("err" in src) return {
|
|
8423
|
+
ok: false,
|
|
8424
|
+
error: src.err
|
|
8425
|
+
};
|
|
8426
|
+
const dst = await fsOpsTarget(ctx, sc.session, toRel);
|
|
8427
|
+
if ("err" in dst) return {
|
|
8428
|
+
ok: false,
|
|
8429
|
+
error: dst.err
|
|
8430
|
+
};
|
|
8431
|
+
if (!await stat(src.abs).catch(() => null)) return {
|
|
8432
|
+
ok: false,
|
|
8433
|
+
error: "源不存在"
|
|
8434
|
+
};
|
|
8435
|
+
try {
|
|
8436
|
+
await rename(src.abs, dst.abs);
|
|
8437
|
+
} catch (error) {
|
|
8438
|
+
const code = fsErrCode(error);
|
|
8439
|
+
return {
|
|
8440
|
+
ok: false,
|
|
8441
|
+
error: code === "EEXIST" || code === "ENOTEMPTY" || code === "EPERM" ? "已存在同名文件或目录" : "重命名失败:" + String(error)
|
|
8442
|
+
};
|
|
8443
|
+
}
|
|
8444
|
+
invalidateIndex(ctx, sc.cwd, fromRel);
|
|
8445
|
+
invalidateIndex(ctx, sc.cwd, toRel);
|
|
8446
|
+
return {
|
|
8447
|
+
ok: true,
|
|
8448
|
+
from: fromRel,
|
|
8449
|
+
to: toRel
|
|
8450
|
+
};
|
|
8451
|
+
},
|
|
8452
|
+
"edrv.fsDelete": async (args) => {
|
|
8453
|
+
const sc = await requireSession(ctx, args.sessionId);
|
|
8454
|
+
if ("err" in sc) return {
|
|
8455
|
+
ok: false,
|
|
8456
|
+
error: sc.err
|
|
8457
|
+
};
|
|
8458
|
+
const rel = normalizeRel(args.path);
|
|
8459
|
+
if (rel === null || rel === "") return {
|
|
8460
|
+
ok: false,
|
|
8461
|
+
error: "路径不合法"
|
|
8462
|
+
};
|
|
8463
|
+
const r = await fsOpsTarget(ctx, sc.session, rel);
|
|
8464
|
+
if ("err" in r) return {
|
|
8465
|
+
ok: false,
|
|
8466
|
+
error: r.err
|
|
8467
|
+
};
|
|
8468
|
+
try {
|
|
8469
|
+
await rm(r.abs, { recursive: true });
|
|
8470
|
+
} catch (error) {
|
|
8471
|
+
return {
|
|
8472
|
+
ok: false,
|
|
8473
|
+
error: "删除失败:" + String(error)
|
|
8474
|
+
};
|
|
8475
|
+
}
|
|
8476
|
+
invalidateIndex(ctx, sc.cwd, rel);
|
|
8477
|
+
return {
|
|
8478
|
+
ok: true,
|
|
8479
|
+
path: rel
|
|
8480
|
+
};
|
|
8481
|
+
},
|
|
8482
|
+
"edrv.fsCopy": async (args) => {
|
|
8483
|
+
const sc = await requireSession(ctx, args.sessionId);
|
|
8484
|
+
if ("err" in sc) return {
|
|
8485
|
+
ok: false,
|
|
8486
|
+
error: sc.err
|
|
8487
|
+
};
|
|
8488
|
+
const r = await fsTransfer(ctx, sc, args, "copy");
|
|
8489
|
+
if ("err" in r) return {
|
|
8490
|
+
ok: false,
|
|
8491
|
+
error: r.err
|
|
8492
|
+
};
|
|
8493
|
+
return {
|
|
8494
|
+
ok: true,
|
|
8495
|
+
from: r.from,
|
|
8496
|
+
to: r.to
|
|
8497
|
+
};
|
|
8498
|
+
},
|
|
8499
|
+
"edrv.fsMove": async (args) => {
|
|
8500
|
+
const sc = await requireSession(ctx, args.sessionId);
|
|
8501
|
+
if ("err" in sc) return {
|
|
8502
|
+
ok: false,
|
|
8503
|
+
error: sc.err
|
|
8504
|
+
};
|
|
8505
|
+
const r = await fsTransfer(ctx, sc, args, "move");
|
|
8506
|
+
if ("err" in r) return {
|
|
8507
|
+
ok: false,
|
|
8508
|
+
error: r.err
|
|
8509
|
+
};
|
|
8510
|
+
return {
|
|
8511
|
+
ok: true,
|
|
8512
|
+
from: r.from,
|
|
8513
|
+
to: r.to
|
|
8514
|
+
};
|
|
8515
|
+
},
|
|
8143
8516
|
"mcp.list": async () => ({
|
|
8144
8517
|
ok: true,
|
|
8145
8518
|
...listMcp(ctx)
|