dsh-vscode-mode 0.11.0 → 0.12.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 +31 -10
- package/lib/client.js +1092 -475
- package/lib/client.js.map +1 -1
- package/lib/index.js +91 -41
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/activePathStore.ts +28 -0
- package/src/client/commandBridge.ts +9 -41
- package/src/client/index.ts +91 -14
- package/src/client/keybindings.ts +19 -73
- package/src/client/shortcutsOfficial.ts +451 -0
- package/src/client/sidebar/panels/RulesPanel.ts +15 -11
- package/src/client/ui/EditorView.ts +10 -83
- package/src/client/ui/McpSettings.ts +4 -5
- package/src/client/ui/QuickOpen.ts +10 -11
- package/src/client/ui/ShortcutSettings.ts +294 -0
- package/src/client/ui/commandCatalog.ts +36 -41
- package/src/fileOpenSettings.ts +3 -0
- package/src/rules.ts +100 -44
- package/src/shared/keybindings.ts +253 -0
- package/src/shared/rules.ts +9 -1
- package/src/client/ui/KeybindingsPanel.ts +0 -190
package/lib/index.js
CHANGED
|
@@ -118,6 +118,8 @@ async function refreshMcp(ctx, id) {
|
|
|
118
118
|
* `|` 连接多个候选(任一命中即触发),如 `Alt+ArrowLeft|Ctrl+Alt+-`。
|
|
119
119
|
* 约束:本模块被 host 与 client 两半共用,**禁止 import client 侧模块**(React/浏览器 API)。
|
|
120
120
|
* 与 client/ui/commandCatalog 的一致性由 tests/commands.test.ts 断言兜底。
|
|
121
|
+
* 官方机制(2026-10):SHORTCUT_PROFILES 描述各命令在官方 ctx.shortcuts 的 profile 默认键位
|
|
122
|
+
* (由 client 注册官方服务时使用);chordToBinding/bindingToChord 负责插件弦与官方绑定互转。
|
|
121
123
|
* 作者 ddj 2026年08月26号 / 2026年09月10号
|
|
122
124
|
*/
|
|
123
125
|
/** 命令 id → 默认键位(可为多候选)。命令目录以此为准,新增命令只需加一项。 */
|
|
@@ -900,6 +902,9 @@ async function updateSection(provider, ns, patch, expectedRevision) {
|
|
|
900
902
|
* 构建设置 section schema(install 路径与插件 Config 声明共用,保证两代形状同源)。
|
|
901
903
|
* 字段集 = fileOpenTool/keybindings/sidebarMinWidth/maxOpenEditors/integrationBaseUrl/
|
|
902
904
|
* aiInline/aiProvider/aiModel/aiEffort/svnPath/tortoisePath/nativeOpenExts,全部带默认值
|
|
905
|
+
* (keybindings 自 2026-10 起废弃停写:键位持久化归官方 shortcuts 存储
|
|
906
|
+
* userData/keybindings.json / dsh.keybindings.v1;字段保留仅为旧档迁移读取与降级回滚,
|
|
907
|
+
* 见 client/shortcutsOfficial.ts 的 migrateLegacyKeybindings。)
|
|
903
908
|
* (Config 启动校验在 undefined/空配置下自动填充,rc/alpha 两代 cordis 均通过)。
|
|
904
909
|
* options.volatile:DSH 0.1.7 线的 Config 导出专用——SettingsForms.describe 只下发
|
|
905
910
|
* 含 volatile 字段的 entry(volatileForm 门槛),不标记则 ns 永不出现在 describe、
|
|
@@ -5429,7 +5434,7 @@ async function restoreFile(ctx, session, rec) {
|
|
|
5429
5434
|
//#region src/rules.ts
|
|
5430
5435
|
/**
|
|
5431
5436
|
* dsh-vscode-mode host — 规则管理(Codebuddy/Cursor 式 .mdc 规则)。
|
|
5432
|
-
* - 存储:用户规则 ~/.dsh/rules
|
|
5437
|
+
* - 存储:用户规则 DSH_HOME/rules + ~/.dsh/rules(当前目录优先);项目规则 <工作区>/.dsh/rules。
|
|
5433
5438
|
* - 格式:frontmatter(description / alwaysApply / globs / enabled)+ markdown 正文;
|
|
5434
5439
|
* 类型映射 总是=always / 自动=auto(globs) / 手动=manual(仅索引);enabled 为本插件扩展字段,缺省 true。
|
|
5435
5440
|
* - 生效:host 注册 systemPrompt.section(order 400),每次装配同步读取(mtime 缓存),
|
|
@@ -5452,7 +5457,7 @@ const SECTION_NAME = "dsh-vscode-mode:rules";
|
|
|
5452
5457
|
/** section 顺序:persona(0) 与 PLAN_POLICY(500) 之间,规则先于计划策略被读到。 */
|
|
5453
5458
|
const SECTION_ORDER = 400;
|
|
5454
5459
|
/**
|
|
5455
|
-
*
|
|
5460
|
+
* 当前用户规则目录(DSH_HOME/rules)。
|
|
5456
5461
|
* @author ddj 2026年09月03号
|
|
5457
5462
|
* @param home DSH home(缺省自动解析)
|
|
5458
5463
|
* @returns 绝对路径
|
|
@@ -5460,6 +5465,27 @@ const SECTION_ORDER = 400;
|
|
|
5460
5465
|
function userRulesDir(home = dshHome()) {
|
|
5461
5466
|
return join(home, "rules");
|
|
5462
5467
|
}
|
|
5468
|
+
/** DSH_HOME 改变时仍读取系统用户目录的旧规则;相同路径不重复加载。 */
|
|
5469
|
+
function userRuleDirs(activeHome = dshHome(), systemHome = homedir()) {
|
|
5470
|
+
const current = resolve(userRulesDir(activeHome));
|
|
5471
|
+
const legacy = resolve(userRulesDir(join(systemHome, ".dsh")));
|
|
5472
|
+
const key = (path) => process.platform === "win32" ? path.toLowerCase() : path;
|
|
5473
|
+
return key(current) === key(legacy) ? { current } : {
|
|
5474
|
+
current,
|
|
5475
|
+
legacy
|
|
5476
|
+
};
|
|
5477
|
+
}
|
|
5478
|
+
function userDirOf(origin, dirs) {
|
|
5479
|
+
if (origin === void 0 || origin === "current") return dirs.current;
|
|
5480
|
+
if (origin === "legacy" && dirs.legacy) return dirs.legacy;
|
|
5481
|
+
throw new Error("无效的用户规则来源");
|
|
5482
|
+
}
|
|
5483
|
+
/** 同名时当前目录优先;禁用的当前规则也应遮蔽旧目录规则。 */
|
|
5484
|
+
function mergeUserRules(current, legacy, fileOf) {
|
|
5485
|
+
const key = (rule) => process.platform === "win32" ? fileOf(rule).toLowerCase() : fileOf(rule);
|
|
5486
|
+
const seen = new Set(current.map(key));
|
|
5487
|
+
return [...current, ...legacy.filter((rule) => !seen.has(key(rule)))].sort((a, b) => fileOf(a).localeCompare(fileOf(b)));
|
|
5488
|
+
}
|
|
5463
5489
|
/**
|
|
5464
5490
|
* 项目规则目录(<工作区>/.dsh/rules)。
|
|
5465
5491
|
* @author ddj 2026年09月03号
|
|
@@ -5663,18 +5689,28 @@ function renderRulesSection(user, project, workspacePath) {
|
|
|
5663
5689
|
return [renderScope("用户规则(用户配置,必须遵守)", user), workspacePath ? renderScope("项目规则(工作区 " + workspacePath + ",必须遵守)", project) : ""].filter(Boolean).join("\n");
|
|
5664
5690
|
}
|
|
5665
5691
|
/** 单目录规则元信息列表(仅 *.mdc,最多 RULE_DIR_CAP 个;目录缺失返回空)。 */
|
|
5666
|
-
async function listRulesDir(dir, scope) {
|
|
5667
|
-
const mdcs = (await readdir(dir).catch(() =>
|
|
5692
|
+
async function listRulesDir(dir, scope, origin) {
|
|
5693
|
+
const mdcs = (await readdir(dir).catch((error) => {
|
|
5694
|
+
if (error.code === "ENOENT") return [];
|
|
5695
|
+
throw error;
|
|
5696
|
+
})).filter((name) => name.endsWith(".mdc")).sort().slice(0, RULE_DIR_CAP);
|
|
5668
5697
|
const out = [];
|
|
5669
5698
|
for (const file of mdcs) {
|
|
5670
5699
|
const absPath = join(dir, file);
|
|
5671
|
-
const info = await stat(absPath).catch(() =>
|
|
5700
|
+
const info = await stat(absPath).catch((error) => {
|
|
5701
|
+
if (error.code === "ENOENT") return null;
|
|
5702
|
+
throw error;
|
|
5703
|
+
});
|
|
5672
5704
|
if (!info || !info.isFile()) continue;
|
|
5673
|
-
const parsed = parseRuleMdc(await readFile(absPath, "utf8")
|
|
5674
|
-
out.push(toRuleInfo(scope, file, absPath, parsed, info.size, info.mtimeMs));
|
|
5705
|
+
const parsed = parseRuleMdc(await readFile(absPath, "utf8"));
|
|
5706
|
+
out.push(toRuleInfo(scope, file, absPath, parsed, info.size, info.mtimeMs, origin));
|
|
5675
5707
|
}
|
|
5676
5708
|
return out;
|
|
5677
5709
|
}
|
|
5710
|
+
/** 列表与注入共用当前目录优先的合并约定。 */
|
|
5711
|
+
async function listUserRules(dirs = userRuleDirs()) {
|
|
5712
|
+
return mergeUserRules(await listRulesDir(dirs.current, "user", "current"), dirs.legacy ? await listRulesDir(dirs.legacy, "user", "legacy") : [], (rule) => rule.file);
|
|
5713
|
+
}
|
|
5678
5714
|
/** 已注册 workspace 列表(项目 Tab 与写入校验共用)。 */
|
|
5679
5715
|
function workspaceList$1(ctx) {
|
|
5680
5716
|
return (ctx.get("workspaceRegistry")?.list?.() ?? []).filter((item) => Boolean(item) && typeof item.path === "string" && item.path !== "");
|
|
@@ -5691,10 +5727,16 @@ function requireWorkspace$2(ctx, workspacePath) {
|
|
|
5691
5727
|
if (!workspace) throw new Error("项目未注册为 DSH workspace,不能管理项目规则");
|
|
5692
5728
|
return workspace;
|
|
5693
5729
|
}
|
|
5694
|
-
/**
|
|
5695
|
-
function dirOf$1(ctx,
|
|
5696
|
-
|
|
5697
|
-
|
|
5730
|
+
/** 按作用域解析规则目录;客户端路径和未识别的来源均不作为目标使用。 */
|
|
5731
|
+
function dirOf$1(ctx, ref, dirs) {
|
|
5732
|
+
const invalid = validateRuleFile(ref.file);
|
|
5733
|
+
if (invalid) throw new Error(invalid);
|
|
5734
|
+
if (ref.scope === "project") {
|
|
5735
|
+
if (ref.origin !== void 0) throw new Error("项目规则不能指定用户规则来源");
|
|
5736
|
+
return projectRulesDir(requireWorkspace$2(ctx, ref.workspacePath).path);
|
|
5737
|
+
}
|
|
5738
|
+
if (ref.scope !== "user") throw new Error("无效的规则作用域");
|
|
5739
|
+
return userDirOf(ref.origin, dirs);
|
|
5698
5740
|
}
|
|
5699
5741
|
/**
|
|
5700
5742
|
* 规则总列表:用户规则 + 各已注册工作区的项目规则。
|
|
@@ -5702,8 +5744,8 @@ function dirOf$1(ctx, scope, workspacePath) {
|
|
|
5702
5744
|
* @param ctx DSH 上下文
|
|
5703
5745
|
* @returns rules.list 载荷
|
|
5704
5746
|
*/
|
|
5705
|
-
async function rulesList(ctx) {
|
|
5706
|
-
const user = await
|
|
5747
|
+
async function rulesList(ctx, dirs = userRuleDirs()) {
|
|
5748
|
+
const user = await listUserRules(dirs);
|
|
5707
5749
|
const projects = [];
|
|
5708
5750
|
for (const workspace of workspaceList$1(ctx)) {
|
|
5709
5751
|
const dir = projectRulesDir(workspace.path);
|
|
@@ -5728,8 +5770,8 @@ async function rulesList(ctx) {
|
|
|
5728
5770
|
* @param ref 作用域 + 文件名(+ 项目工作区)
|
|
5729
5771
|
* @returns 文件全文
|
|
5730
5772
|
*/
|
|
5731
|
-
async function rulesRead(ctx, ref) {
|
|
5732
|
-
const dir = dirOf$1(ctx, ref
|
|
5773
|
+
async function rulesRead(ctx, ref, dirs = userRuleDirs()) {
|
|
5774
|
+
const dir = dirOf$1(ctx, ref, dirs);
|
|
5733
5775
|
return readFile(join(dir, ref.file), "utf8");
|
|
5734
5776
|
}
|
|
5735
5777
|
/**
|
|
@@ -5743,12 +5785,13 @@ async function rulesRead(ctx, ref) {
|
|
|
5743
5785
|
* @param mtime 修改时间毫秒
|
|
5744
5786
|
* @returns 规则元信息
|
|
5745
5787
|
*/
|
|
5746
|
-
function toRuleInfo(scope, file, absPath, parsed, size, mtime) {
|
|
5788
|
+
function toRuleInfo(scope, file, absPath, parsed, size, mtime, origin) {
|
|
5747
5789
|
return {
|
|
5748
5790
|
scope,
|
|
5749
5791
|
file,
|
|
5750
5792
|
absPath,
|
|
5751
|
-
|
|
5793
|
+
...scope === "user" ? { origin: origin ?? "current" } : {},
|
|
5794
|
+
relHint: scope === "project" ? ".dsh/rules/" : origin === "legacy" ? "~/.dsh/rules/" : "rules/",
|
|
5752
5795
|
description: parsed.description,
|
|
5753
5796
|
type: ruleTypeOf(parsed),
|
|
5754
5797
|
globs: parsed.globs,
|
|
@@ -5765,10 +5808,8 @@ function toRuleInfo(scope, file, absPath, parsed, size, mtime) {
|
|
|
5765
5808
|
* @param input 保存入参
|
|
5766
5809
|
* @returns 保存后的规则元信息
|
|
5767
5810
|
*/
|
|
5768
|
-
async function rulesSave(ctx, input) {
|
|
5769
|
-
const
|
|
5770
|
-
if (invalid) throw new Error(invalid);
|
|
5771
|
-
const dir = dirOf$1(ctx, input.scope, input.workspacePath);
|
|
5811
|
+
async function rulesSave(ctx, input, dirs = userRuleDirs()) {
|
|
5812
|
+
const dir = dirOf$1(ctx, input, dirs);
|
|
5772
5813
|
await mkdir(dir, { recursive: true });
|
|
5773
5814
|
const absPath = join(dir, input.file);
|
|
5774
5815
|
const content = String(input.content ?? "");
|
|
@@ -5779,15 +5820,15 @@ async function rulesSave(ctx, input) {
|
|
|
5779
5820
|
await fs.writeText(target, content, void 0, void 0, fullPolicy$2(ctx));
|
|
5780
5821
|
} else await writeFile(absPath, content, "utf8");
|
|
5781
5822
|
const info = await stat(absPath).catch(() => null);
|
|
5782
|
-
return toRuleInfo(input.scope, input.file, absPath, parseRuleMdc(content), info?.size ?? Buffer$1.byteLength(content), info?.mtimeMs ?? Date.now());
|
|
5823
|
+
return toRuleInfo(input.scope, input.file, absPath, parseRuleMdc(content), info?.size ?? Buffer$1.byteLength(content), info?.mtimeMs ?? Date.now(), input.origin);
|
|
5783
5824
|
}
|
|
5784
5825
|
/**
|
|
5785
5826
|
* 删除一条规则文件(project 作用域同样先过 workspace 注册校验)。
|
|
5786
5827
|
* @author ddj 2026年09月03号
|
|
5787
5828
|
* @param ref 作用域 + 文件名(+ 项目工作区)
|
|
5788
5829
|
*/
|
|
5789
|
-
async function rulesRemove(ctx, ref) {
|
|
5790
|
-
const dir = dirOf$1(ctx, ref
|
|
5830
|
+
async function rulesRemove(ctx, ref, dirs = userRuleDirs()) {
|
|
5831
|
+
const dir = dirOf$1(ctx, ref, dirs);
|
|
5791
5832
|
await rm(join(dir, ref.file), { force: true });
|
|
5792
5833
|
}
|
|
5793
5834
|
/**
|
|
@@ -5797,8 +5838,8 @@ async function rulesRemove(ctx, ref) {
|
|
|
5797
5838
|
* @param enabled 目标状态
|
|
5798
5839
|
* @returns 更新后的规则元信息
|
|
5799
5840
|
*/
|
|
5800
|
-
async function rulesToggle(ctx, ref, enabled) {
|
|
5801
|
-
const dir = dirOf$1(ctx, ref
|
|
5841
|
+
async function rulesToggle(ctx, ref, enabled, dirs = userRuleDirs()) {
|
|
5842
|
+
const dir = dirOf$1(ctx, ref, dirs);
|
|
5802
5843
|
const absPath = join(dir, ref.file);
|
|
5803
5844
|
const text = await readRuleText(ctx, ref, absPath);
|
|
5804
5845
|
if (text === null) throw new Error("规则文件不存在:" + ref.file);
|
|
@@ -5809,7 +5850,10 @@ async function rulesToggle(ctx, ref, enabled) {
|
|
|
5809
5850
|
}
|
|
5810
5851
|
/** 按作用域读规则全文:project 与写盘同通道走 ctx fs,user 走 node fs;缺失返回 null。 */
|
|
5811
5852
|
async function readRuleText(ctx, ref, absPath) {
|
|
5812
|
-
if (ref.scope !== "project") return readFile(absPath, "utf8").catch(() =>
|
|
5853
|
+
if (ref.scope !== "project") return readFile(absPath, "utf8").catch((error) => {
|
|
5854
|
+
if (error.code === "ENOENT") return null;
|
|
5855
|
+
throw error;
|
|
5856
|
+
});
|
|
5813
5857
|
const fs = ctx.get("fs");
|
|
5814
5858
|
if (!fs) throw new Error("缺少 fs 服务");
|
|
5815
5859
|
const target = await fs.resolve(".dsh/rules/" + ref.file, { cwd: requireWorkspace$2(ctx, ref.workspacePath).path });
|
|
@@ -5833,24 +5877,24 @@ async function rulesSaveContent(ctx, ref, absPath, content) {
|
|
|
5833
5877
|
/** 重建单条规则元信息(toggle 后回传 UI;stat 失败时用内容长度 + 当前时间兜底)。 */
|
|
5834
5878
|
async function ruleInfoOf(ref, absPath, content) {
|
|
5835
5879
|
const info = await stat(absPath).catch(() => null);
|
|
5836
|
-
return toRuleInfo(ref.scope, ref.file, absPath, parseRuleMdc(content), info?.size ?? Buffer$1.byteLength(content), info?.mtimeMs ?? Date.now());
|
|
5880
|
+
return toRuleInfo(ref.scope, ref.file, absPath, parseRuleMdc(content), info?.size ?? Buffer$1.byteLength(content), info?.mtimeMs ?? Date.now(), ref.origin);
|
|
5837
5881
|
}
|
|
5838
5882
|
const injCache = /* @__PURE__ */ new Map();
|
|
5839
5883
|
/**
|
|
5840
|
-
* 同步读取一个规则目录(装配 provider 内专用):仅 *.mdc,mtime
|
|
5884
|
+
* 同步读取一个规则目录(装配 provider 内专用):仅 *.mdc,mtime 缓存;缺失视为空。
|
|
5841
5885
|
* @author ddj 2026年09月03号
|
|
5842
5886
|
* @param dir 规则目录
|
|
5843
5887
|
* @param scope 作用域
|
|
5844
|
-
* @param
|
|
5888
|
+
* @param origin 用户规则来源
|
|
5845
5889
|
* @returns 已加载规则列表
|
|
5846
5890
|
*/
|
|
5847
|
-
function readRulesSync(dir, scope) {
|
|
5848
|
-
if (!existsSync(dir)) return [];
|
|
5891
|
+
function readRulesSync(dir, scope, origin) {
|
|
5849
5892
|
let names;
|
|
5850
5893
|
try {
|
|
5851
5894
|
names = readdirSync(dir);
|
|
5852
|
-
} catch {
|
|
5853
|
-
return [];
|
|
5895
|
+
} catch (error) {
|
|
5896
|
+
if (error.code === "ENOENT") return [];
|
|
5897
|
+
throw error;
|
|
5854
5898
|
}
|
|
5855
5899
|
const out = [];
|
|
5856
5900
|
for (const file of names.filter((name) => name.endsWith(".mdc")).sort().slice(0, RULE_DIR_CAP)) {
|
|
@@ -5858,8 +5902,9 @@ function readRulesSync(dir, scope) {
|
|
|
5858
5902
|
let info;
|
|
5859
5903
|
try {
|
|
5860
5904
|
info = statSync(absPath);
|
|
5861
|
-
} catch {
|
|
5862
|
-
continue;
|
|
5905
|
+
} catch (error) {
|
|
5906
|
+
if (error.code === "ENOENT") continue;
|
|
5907
|
+
throw error;
|
|
5863
5908
|
}
|
|
5864
5909
|
if (!info.isFile()) continue;
|
|
5865
5910
|
const cached = injCache.get(absPath);
|
|
@@ -5868,8 +5913,9 @@ function readRulesSync(dir, scope) {
|
|
|
5868
5913
|
else {
|
|
5869
5914
|
try {
|
|
5870
5915
|
parsed = parseRuleMdc(readFileSync(absPath, "utf8"));
|
|
5871
|
-
} catch {
|
|
5872
|
-
continue;
|
|
5916
|
+
} catch (error) {
|
|
5917
|
+
if (error.code === "ENOENT") continue;
|
|
5918
|
+
throw error;
|
|
5873
5919
|
}
|
|
5874
5920
|
injCache.set(absPath, {
|
|
5875
5921
|
mtimeMs: info.mtimeMs,
|
|
@@ -5878,12 +5924,16 @@ function readRulesSync(dir, scope) {
|
|
|
5878
5924
|
});
|
|
5879
5925
|
}
|
|
5880
5926
|
out.push({
|
|
5881
|
-
info: toRuleInfo(scope, file, absPath, parsed, info.size, info.mtimeMs),
|
|
5927
|
+
info: toRuleInfo(scope, file, absPath, parsed, info.size, info.mtimeMs, origin),
|
|
5882
5928
|
body: parsed.body
|
|
5883
5929
|
});
|
|
5884
5930
|
}
|
|
5885
5931
|
return out;
|
|
5886
5932
|
}
|
|
5933
|
+
/** 同步装配与列表同名遮蔽策略一致。 */
|
|
5934
|
+
function readUserRulesSync(dirs = userRuleDirs()) {
|
|
5935
|
+
return mergeUserRules(readRulesSync(dirs.current, "user", "current"), dirs.legacy ? readRulesSync(dirs.legacy, "user", "legacy") : [], (rule) => rule.info.file);
|
|
5936
|
+
}
|
|
5887
5937
|
/** 从装配上下文解析会话工作区 cwd(agent.id → sessions → header.cwd;缺链返回 null)。 */
|
|
5888
5938
|
function cwdFromAssemble(ctx, asm) {
|
|
5889
5939
|
const id = asm?.agent?.id;
|
|
@@ -5897,12 +5947,12 @@ function cwdFromAssemble(ctx, asm) {
|
|
|
5897
5947
|
* @param ctx DSH host 上下文
|
|
5898
5948
|
* @returns 是否成功注册
|
|
5899
5949
|
*/
|
|
5900
|
-
function installRulesSection(ctx) {
|
|
5950
|
+
function installRulesSection(ctx, dirs = userRuleDirs()) {
|
|
5901
5951
|
const sp = ctx.get("systemPrompt");
|
|
5902
5952
|
if (!sp || typeof sp.section !== "function") return false;
|
|
5903
5953
|
const provider = (asm) => {
|
|
5904
5954
|
try {
|
|
5905
|
-
const user =
|
|
5955
|
+
const user = readUserRulesSync(dirs);
|
|
5906
5956
|
const cwd = cwdFromAssemble(ctx, asm);
|
|
5907
5957
|
return renderRulesSection(user, cwd ? readRulesSync(projectRulesDir(cwd), "project") : [], cwd ?? void 0);
|
|
5908
5958
|
} catch {
|