@michengai/dsh-skills-manager 0.1.25 → 0.1.26
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/CHANGELOG.md +8 -7
- package/CHANGELOG.zh-CN.md +8 -7
- package/README.md +4 -10
- package/README.zh-CN.md +4 -10
- package/assets/screenshots/delete-plugin.png +0 -0
- package/assets/screenshots/skill-detail.png +0 -0
- package/assets/screenshots/skills-manager-v2-preview.png +0 -0
- package/lib/client.js +10 -3
- package/lib/core.js +13 -2
- package/lib/index.js +2 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
The five most recent published versions are listed below.
|
|
6
6
|
|
|
7
|
+
## 0.1.26 — 2026-08-27
|
|
8
|
+
|
|
9
|
+
- Fixed browser folder imports that exceeded the former 16 MiB Base64 JSON request ceiling while still fitting within the documented 25 MiB decoded-content limit.
|
|
10
|
+
- Added client-side checks for the existing 10 MiB ZIP, 5 MiB per-file, 25 MiB total, and 500-entry upload limits so oversized selections fail with a specific message before being read.
|
|
11
|
+
- Replaced the large-string Base64 validation regex with stack-safe validation to prevent multi-MiB files from exhausting the JavaScript call stack.
|
|
12
|
+
|
|
13
|
+
Published package: [`@michengai/dsh-skills-manager@0.1.26`](https://www.npmjs.com/package/@michengai/dsh-skills-manager/v/0.1.26).
|
|
14
|
+
|
|
7
15
|
## 0.1.25 — 2026-08-27
|
|
8
16
|
|
|
9
17
|
- Added provider-backed loading for public Agents, Codex, Claude, Gemini, and OpenCode while keeping external source files read-only and persisting toggles in manager state.
|
|
@@ -31,10 +39,3 @@ Release tag: [`v0.1.23`](https://github.com/MichengAI/dsh-skills-manager/tree/v0
|
|
|
31
39
|
- Preserved symlink-rejection errors while collecting batch import candidates.
|
|
32
40
|
|
|
33
41
|
Release tag: [`v0.1.22`](https://github.com/MichengAI/dsh-skills-manager/tree/v0.1.22).
|
|
34
|
-
|
|
35
|
-
## 0.1.21 — 2026-08-17
|
|
36
|
-
|
|
37
|
-
- Applied the same symlink and depth checks during import dry runs.
|
|
38
|
-
- Aligned dry-run cleanup, deletion handling, and HEAD behavior.
|
|
39
|
-
|
|
40
|
-
Release tag: [`v0.1.21`](https://github.com/MichengAI/dsh-skills-manager/tree/v0.1.21).
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
以下记录最近发布的五个版本。
|
|
6
6
|
|
|
7
|
+
## 0.1.26 — 2026-08-27
|
|
8
|
+
|
|
9
|
+
- 修复浏览器上传文件夹时,原始内容仍在 25 MiB 总量限制内,却因 Base64 JSON 超过旧 16 MiB 请求体上限而失败的问题。
|
|
10
|
+
- 在浏览器读取内容前检查既有的 ZIP 10 MiB、单文件 5 MiB、总量 25 MiB 和 500 个条目限制,超限时直接显示具体原因。
|
|
11
|
+
- 将大字符串 Base64 正则校验改为栈安全校验,避免数 MiB 文件耗尽 JavaScript 调用栈。
|
|
12
|
+
|
|
13
|
+
发布包:[`@michengai/dsh-skills-manager@0.1.26`](https://www.npmjs.com/package/@michengai/dsh-skills-manager/v/0.1.26)。
|
|
14
|
+
|
|
7
15
|
## 0.1.25 — 2026-08-27
|
|
8
16
|
|
|
9
17
|
- 新增公共 Agent、Codex、Claude、Gemini 与 OpenCode 技能源加载;启停策略仅写入管理器状态,不修改外部源文件。
|
|
@@ -31,10 +39,3 @@
|
|
|
31
39
|
- 批量收集导入候选项时不再吞掉符号链接拒绝错误。
|
|
32
40
|
|
|
33
41
|
发布标签:[`v0.1.22`](https://github.com/MichengAI/dsh-skills-manager/tree/v0.1.22)。
|
|
34
|
-
|
|
35
|
-
## 0.1.21 — 2026-08-17
|
|
36
|
-
|
|
37
|
-
- 导入 dry-run 阶段执行与正式导入一致的符号链接和目录深度检查。
|
|
38
|
-
- 统一 dry-run 清理、删除处理和 HEAD 请求行为。
|
|
39
|
-
|
|
40
|
-
发布标签:[`v0.1.21`](https://github.com/MichengAI/dsh-skills-manager/tree/v0.1.21)。
|
package/README.md
CHANGED
|
@@ -34,17 +34,13 @@ Browse by source or search in **Settings → Skills**. External Agent sources ar
|
|
|
34
34
|
|
|
35
35
|

|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Open any skill to inspect its source path, diagnostics, Markdown body, and parsed frontmatter:
|
|
38
38
|
|
|
39
|
-

|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Moving a DSH-local skill to Trash requires confirmation and remains recoverable until it is permanently deleted:
|
|
42
42
|
|
|
43
|
-

|
|
43
|
+

|
|
48
44
|
|
|
49
45
|
## DSH product ecosystem
|
|
50
46
|
|
|
@@ -145,8 +141,6 @@ Open **Settings → Skills**, then use the panel as follows:
|
|
|
145
141
|
| Create from conversation | Let an Agent call `create_skill`; DSH asks for approval before writing. | `$DSH_HOME/skills` |
|
|
146
142
|
| Delete and recover | Move to Trash, restore, or permanently delete in a second step. | DSH-local skills |
|
|
147
143
|
|
|
148
|
-

|
|
149
|
-
|
|
150
144
|
> Toggling a shared source never changes its files; only DSH-local skills can move to Trash.
|
|
151
145
|
|
|
152
146
|
Escape closes only the frontmost upload or confirmation dialog and leaves Settings open.
|
package/README.zh-CN.md
CHANGED
|
@@ -34,17 +34,13 @@
|
|
|
34
34
|
|
|
35
35
|

|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
打开任意技能可查看来源路径、诊断结果、Markdown 正文与解析后的 frontmatter:
|
|
38
38
|
|
|
39
|
-

|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
DSH 本地技能移入回收站前需要确认;永久删除前仍可恢复:
|
|
42
42
|
|
|
43
|
-

|
|
43
|
+

|
|
48
44
|
|
|
49
45
|
## DSH 产品生态
|
|
50
46
|
|
|
@@ -145,8 +141,6 @@ dsh --profile web --dump-config
|
|
|
145
141
|
| 从对话创建 | 让 Agent 调用 `create_skill`;写入前由 DSH 审批界面确认。 | `$DSH_HOME\skills` |
|
|
146
142
|
| 删除与恢复 | 删除先进入回收站;可恢复或永久二次删除。 | DSH 本地技能 |
|
|
147
143
|
|
|
148
|
-

|
|
149
|
-
|
|
150
144
|
> 共享来源的启停不会修改源文件;只有 DSH 本地技能可以移入回收站。
|
|
151
145
|
|
|
152
146
|
按 ESC 只关闭最上层上传框或确认框,设置页会保持打开。
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/client.js
CHANGED
|
@@ -112,18 +112,25 @@ window.__ModuleLoader__.load({
|
|
|
112
112
|
function normalizeSkillQuery(query) { return String(query == null ? "" : query).trim().toLowerCase(); }
|
|
113
113
|
function matchSkillQuery(skill, query) { var q = normalizeSkillQuery(query); if (!q) return true; return [skill.name, skill.declaredName, skill.description, skill.kind, skill.kindLabel, skill.statusLabel, skill.rootKey, skill.rootLabel].some(function (value) { return String(value == null ? "" : value).toLowerCase().includes(q); }); }
|
|
114
114
|
function filterSkills(list, options) { var rootKey = options && options.rootKey != null ? options.rootKey : ""; return list.filter(function (skill) { return (!rootKey || skill.rootKey === rootKey) && matchSkillQuery(skill, options && options.query); }); }
|
|
115
|
+
var MAX_UPLOAD_ARCHIVE_BYTES = 10 << 20, MAX_UPLOAD_ENTRY_BYTES = 5 << 20, MAX_UPLOAD_TOTAL_BYTES = 25 << 20, MAX_UPLOAD_ENTRIES = 500;
|
|
115
116
|
function uploadFilePath(file) { return String(file && (file._dssmPath || file.webkitRelativePath || file.name) || "").replace(/\\/g, "/"); }
|
|
116
117
|
function inspectUploadSelection(files) {
|
|
117
118
|
var list = Array.prototype.slice.call(files || []); if (!list.length) return null;
|
|
119
|
+
if (list.length > MAX_UPLOAD_ENTRIES) return { error: { code: "error.upload.tooMany", params: { limit: MAX_UPLOAD_ENTRIES } } };
|
|
118
120
|
var relative = list.some(function (file) { return uploadFilePath(file).includes("/"); });
|
|
119
121
|
if (relative) {
|
|
120
122
|
if (!list.some(function (file) { return /(^|\/)skill\.md$/i.test(uploadFilePath(file)); })) return { error: { code: "select.folder.invalid" } };
|
|
121
|
-
|
|
123
|
+
var oversized = list.find(function (file) { return Number(file.size || 0) > MAX_UPLOAD_ENTRY_BYTES; });
|
|
124
|
+
if (oversized) return { error: { code: "error.upload.tooLarge", params: { limit: MAX_UPLOAD_ENTRY_BYTES } } };
|
|
125
|
+
var total = list.reduce(function (sum, file) { return sum + Number(file.size || 0); }, 0);
|
|
126
|
+
if (total > MAX_UPLOAD_TOTAL_BYTES) return { error: { code: "error.upload.tooLarge", params: { limit: MAX_UPLOAD_TOTAL_BYTES } } };
|
|
127
|
+
return { kind: "folder", name: uploadFilePath(list[0]).split("/")[0], files: list, count: list.length, size: total };
|
|
122
128
|
}
|
|
123
129
|
if (list.length !== 1) return { error: { code: "select.file.invalid" } };
|
|
124
130
|
var name = String(list[0].name || ""), lower = name.toLowerCase();
|
|
125
|
-
|
|
126
|
-
if (lower
|
|
131
|
+
var size = Number(list[0].size || 0);
|
|
132
|
+
if (lower === "skill.md") return size > MAX_UPLOAD_ENTRY_BYTES ? { error: { code: "error.upload.tooLarge", params: { limit: MAX_UPLOAD_ENTRY_BYTES } } } : { kind: "skill", name: name, files: list, count: 1, size: size };
|
|
133
|
+
if (lower.endsWith(".zip")) return size > MAX_UPLOAD_ARCHIVE_BYTES ? { error: { code: "error.upload.archiveTooLarge", params: { limit: MAX_UPLOAD_ARCHIVE_BYTES } } } : { kind: "zip", name: name, files: list, count: 1, size: size };
|
|
127
134
|
return { error: { code: "select.file.invalid" } };
|
|
128
135
|
}
|
|
129
136
|
function bytesToBase64(buffer) { var bytes = new Uint8Array(buffer), binary = "", chunk = 0x8000; for (var i = 0; i < bytes.length; i += chunk) binary += String.fromCharCode.apply(null, bytes.subarray(i, Math.min(i + chunk, bytes.length))); return btoa(binary); }
|
package/lib/core.js
CHANGED
|
@@ -1012,11 +1012,22 @@ function normalizeUploadPath(input) {
|
|
|
1012
1012
|
|
|
1013
1013
|
function decodeUploadBase64(value, maxBytes, code = "error.upload.tooLarge") {
|
|
1014
1014
|
const raw = String(value == null ? "" : value);
|
|
1015
|
-
|
|
1015
|
+
const padding = raw.endsWith("==") ? 2 : raw.endsWith("=") ? 1 : 0;
|
|
1016
|
+
const dataLength = raw.length - padding;
|
|
1017
|
+
const firstPadding = raw.indexOf("=");
|
|
1018
|
+
if (raw.length % 4 !== 0 || (firstPadding !== -1 && firstPadding !== dataLength)) {
|
|
1016
1019
|
throw codedError("上传内容不是合法 Base64", "error.upload.encoding");
|
|
1017
1020
|
}
|
|
1021
|
+
const decodedLength = raw.length / 4 * 3 - padding;
|
|
1022
|
+
if (decodedLength > maxBytes) throw codedError(`上传内容超过 ${maxBytes} 字节限制`, code, { limit: maxBytes });
|
|
1023
|
+
// 避免对数 MiB 字符串使用带重复分组的正则;V8 可能在合法大文件上耗尽调用栈。
|
|
1024
|
+
for (let index = 0; index < dataLength; index += 1) {
|
|
1025
|
+
const char = raw.charCodeAt(index);
|
|
1026
|
+
if (!((char >= 65 && char <= 90) || (char >= 97 && char <= 122) || (char >= 48 && char <= 57) || char === 43 || char === 47)) {
|
|
1027
|
+
throw codedError("上传内容不是合法 Base64", "error.upload.encoding");
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1018
1030
|
const bytes = Buffer.from(raw, "base64");
|
|
1019
|
-
if (bytes.length > maxBytes) throw codedError(`上传内容超过 ${maxBytes} 字节限制`, code, { limit: maxBytes });
|
|
1020
1031
|
return bytes;
|
|
1021
1032
|
}
|
|
1022
1033
|
|
package/lib/index.js
CHANGED
|
@@ -27,7 +27,8 @@ const name = "skills-manager";
|
|
|
27
27
|
const inject = ["webServer", "skills", "tools"];
|
|
28
28
|
const CLIENT_MARKER_HEADER = "x-dsh-skills-manager";
|
|
29
29
|
const MAX_LOG_BYTES = 1 << 20;
|
|
30
|
-
|
|
30
|
+
// 文件夹上传允许 25 MiB 原始内容;Base64 会膨胀约 1/3,再为最多 500 条路径预留余量。
|
|
31
|
+
const MAX_UPLOAD_BODY_BYTES = 36 << 20;
|
|
31
32
|
|
|
32
33
|
function makeLog() {
|
|
33
34
|
const file = logPath();
|
package/package.json
CHANGED