@michengai/dsh-skills-manager 1.2.33 → 1.2.34
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 +11 -0
- package/CHANGELOG.zh-CN.md +11 -0
- package/README.en.md +2 -2
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/lib/repositories.js +30 -16
- package/lib/repository-git.js +122 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.34] - 2026-11-06
|
|
4
|
+
|
|
5
|
+
### Improvements
|
|
6
|
+
|
|
7
|
+
- **Load large skill repositories without size-limit errors** (Issue #79):
|
|
8
|
+
- GitHub repositories are now fetched on demand: only directories containing `SKILL.md` are downloaded, so README screenshots and other unrelated assets no longer count against the size limit (the reported 235 MiB repository now transfers about 20 MiB while listing all 177 skills);
|
|
9
|
+
- Repository scanning and installation are no longer capped by the old 32 MiB archive limit; when no usable local Git is available, the plugin falls back to the previous full-archive download;
|
|
10
|
+
- The snapshot version badge now shows the Git commit of GitHub repositories.
|
|
11
|
+
|
|
12
|
+
[中文文档](CHANGELOG.zh-CN.md)
|
|
13
|
+
|
|
3
14
|
## [1.2.33] - 2026-11-05
|
|
4
15
|
|
|
5
16
|
### Improvements
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
|
+
## [1.2.34] - 2026-11-06
|
|
4
|
+
|
|
5
|
+
### 体验优化
|
|
6
|
+
|
|
7
|
+
- **大体积技能仓库不再报体积超限**(Issue #79):
|
|
8
|
+
- GitHub 仓库改为按需检出:只下载包含 `SKILL.md` 的技能目录,仓库首页截图等无关文件不再计入体积限制(反馈中的 235 MiB 仓库现在只传输约 20 MiB,即可列出全部 177 个技能);
|
|
9
|
+
- 仓库扫描与安装不再受旧的 32 MiB 归档上限约束;本机没有可用 Git 时自动回落为整库下载;
|
|
10
|
+
- 扫描版本标识改为显示 GitHub 仓库的 Git 提交。
|
|
11
|
+
|
|
12
|
+
[English](CHANGELOG.md)
|
|
13
|
+
|
|
3
14
|
## [1.2.33] - 2026-11-05
|
|
4
15
|
|
|
5
16
|
### 交互体验优化
|
package/README.en.md
CHANGED
|
@@ -73,7 +73,7 @@ dsh plugin --profile web add @michengai/dsh-skills-manager@latest --registry=htt
|
|
|
73
73
|
|
|
74
74
|
Restart DSH, refresh the page, and open **Settings → Skills**. To update, select **Check for updates** or run the installation command again.
|
|
75
75
|
|
|
76
|
-
- Plugin `1.2.
|
|
76
|
+
- Plugin `1.2.34` is tested with DeepSeek Harness `0.2.0-rc.1`, `0.2.0-rc.2`, `0.2.1-rc.1`, `0.2.1-rc.2`, `0.2.1-rc.3`, `0.2.1-rc.4`, `0.2.1-rc.5`, `0.2.2-rc.1`.
|
|
77
77
|
- Host compatibility: Full DeepSeek Harness `0.2.x` line (`>=0.2.0-rc.1 <0.3.0-0`), forwards-compatible with subsequent `0.2.x` RC releases without requiring routine plugin version bumps. Legacy versions and breaking changes in the `0.3.0` generation are rejected by the version gate. If you still use a `0.1.x` host, install plugin `1.1.23` instead of `@latest`.
|
|
78
78
|
- **Reverse proxy deployment**: Supports hosting DSH behind prefix-stripping reverse proxies (e.g. `/app/dsh/`). API and update endpoints resolve against the page base URI (`document.baseURI`).
|
|
79
79
|
|
|
@@ -159,7 +159,7 @@ The DeepSeek Harness loader enforces strict identifier policies:
|
|
|
159
159
|
|
|
160
160
|
- The header **Check for updates** updates the plugin itself; repository **Refresh** checks subscribed skill contents without replacing local files.
|
|
161
161
|
- Repository installation can target user DSH skills, Shared Agent (`~/.agents/skills`, overridable via `DSH_AGENTS_HOME`), or `<project>/.dsh/skills` for registered workspaces. Unsafe paths and external agent roots (Codex, Claude) are locked against writes.
|
|
162
|
-
-
|
|
162
|
+
- GitHub repositories are fetched on demand: only directories containing `SKILL.md` are downloaded, so README images and other unrelated assets are skipped (a subdirectory narrows the scope further). Without a usable local Git the plugin falls back to the full archive, capped at 32 MiB.
|
|
163
163
|
- Local edits are detected and require explicit confirmation before overwriting; backup history is preserved for rollback.
|
|
164
164
|
|
|
165
165
|
## Supported Agent directories
|
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ dsh plugin --profile web add @michengai/dsh-skills-manager@latest --registry=htt
|
|
|
73
73
|
|
|
74
74
|
安装后重启 DSH 并刷新页面,打开「设置 → 技能」即可使用。更新时可点击「检查更新」,或重新执行安装命令。
|
|
75
75
|
|
|
76
|
-
- `1.2.
|
|
76
|
+
- `1.2.34` 已验证兼容 DeepSeek Harness `0.2.0-rc.1`、`0.2.0-rc.2`、`0.2.1-rc.1`、`0.2.1-rc.2`、`0.2.1-rc.3`、`0.2.1-rc.4`、`0.2.1-rc.5`、`0.2.2-rc.1`。
|
|
77
77
|
- 宿主支持范围:DeepSeek Harness `0.2.x` 全系列(`>=0.2.0-rc.1 <0.3.0-0`),自动支持后续全部 0.2.x RC 版本,同代宿主升级无需重复等待发版。早期版本与 `0.3.0` 代际破坏性更新由版本门拦截。仍在使用 `0.1.x` 的,请安装插件 `1.1.23`,不要装 `@latest`。
|
|
78
78
|
- **反向代理部署**:支持将 DSH 部署在带路径前缀的反向代理后面(如 `/app/dsh/`),插件 API 统一自动按页面基址(`document.baseURI`)相对解析。
|
|
79
79
|
|
|
@@ -159,7 +159,7 @@ DeepSeek Harness 宿主加载机制对技能标识符遵循规范命名约束:
|
|
|
159
159
|
|
|
160
160
|
- 顶栏「检查更新」用于更新技能管理器插件自身;仓库区「刷新」仅检查技能仓库内容更新,不自动覆盖本地。
|
|
161
161
|
- 仓库安装可以写入用户目录下的 DSH skills 或公共 Agent(`~/.agents/skills`,可用 `DSH_AGENTS_HOME` 覆盖),也可以写入已登记工作区的 `<项目根>/.dsh/skills`。选择器显示实际配置路径,并禁用不安全路径;缺少的目录仅在确认安装后创建。不能安装到 Codex、Claude 等其他 Agent 目录。
|
|
162
|
-
-
|
|
162
|
+
- GitHub 仓库按需检出:只下载包含 `SKILL.md` 的技能目录,仓库首页配图等无关内容不参与下载(指定子目录可进一步缩小范围);本机没有可用 Git 时回落整库归档,归档上限 32 MiB。
|
|
163
163
|
- 更新前检测本地修改,覆盖必须明确确认;历史备份不自动清理,可随时恢复上一版。
|
|
164
164
|
|
|
165
165
|
## 支持的 Agent 目录
|
package/README.zh-CN.md
CHANGED
|
@@ -73,7 +73,7 @@ dsh plugin --profile web add @michengai/dsh-skills-manager@latest --registry=htt
|
|
|
73
73
|
|
|
74
74
|
安装后重启 DSH 并刷新页面,打开「设置 → 技能」即可使用。更新时可点击「检查更新」,或重新执行安装命令。
|
|
75
75
|
|
|
76
|
-
- `1.2.
|
|
76
|
+
- `1.2.34` 已验证兼容 DeepSeek Harness `0.2.0-rc.1`、`0.2.0-rc.2`、`0.2.1-rc.1`、`0.2.1-rc.2`、`0.2.1-rc.3`、`0.2.1-rc.4`、`0.2.1-rc.5`、`0.2.2-rc.1`。
|
|
77
77
|
- 宿主支持范围:DeepSeek Harness `0.2.x` 全系列(`>=0.2.0-rc.1 <0.3.0-0`),自动支持后续全部 0.2.x RC 版本,同代宿主升级无需重复等待发版。早期版本与 `0.3.0` 代际破坏性更新由版本门拦截。仍在使用 `0.1.x` 的,请安装插件 `1.1.23`,不要装 `@latest`。
|
|
78
78
|
- **反向代理部署**:支持将 DSH 部署在带路径前缀的反向代理后面(如 `/app/dsh/`),插件 API 统一自动按页面基址(`document.baseURI`)相对解析。
|
|
79
79
|
|
|
@@ -159,7 +159,7 @@ DeepSeek Harness 宿主加载机制对技能标识符遵循规范命名约束:
|
|
|
159
159
|
|
|
160
160
|
- 顶栏「检查更新」用于更新技能管理器插件自身;仓库区「刷新」仅检查技能仓库内容更新,不自动覆盖本地。
|
|
161
161
|
- 仓库安装可以写入用户目录下的 DSH skills 或公共 Agent(`~/.agents/skills`,可用 `DSH_AGENTS_HOME` 覆盖),也可以写入已登记工作区的 `<项目根>/.dsh/skills`。选择器显示实际配置路径,并禁用不安全路径;缺少的目录仅在确认安装后创建。不能安装到 Codex、Claude 等其他 Agent 目录。
|
|
162
|
-
-
|
|
162
|
+
- GitHub 仓库按需检出:只下载包含 `SKILL.md` 的技能目录,仓库首页配图等无关内容不参与下载(指定子目录可进一步缩小范围);本机没有可用 Git 时回落整库归档,归档上限 32 MiB。
|
|
163
163
|
- 更新前检测本地修改,覆盖必须明确确认;历史备份不自动清理,可随时恢复上一版。
|
|
164
164
|
|
|
165
165
|
## 支持的 Agent 目录
|
package/lib/repositories.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
4
4
|
import { unzipSync } from "fflate";
|
|
5
5
|
import { managerHomePath, resolveWorkspaceTarget, repositoryDestinations, trashRootPath, listTrash, restoreTrash as restoreSkillTrash, permanentlyDeleteTrash, parseSkillDoc, importUploadedSkill, deleteSkill, setSkillEnabled, readManagerState, state, createWorkspaceIndex } from "./core.js";
|
|
6
6
|
import { createRepositoryUpdater, fileIndex, signature, readSkillTree, repositoryInstallRoot, recordInstallRoot, selectInstallRecord } from "./repository-updates.js";
|
|
7
|
-
import { parseGitUrl, fetchGitRepository, probeGitRemote, probeGitHubHttp } from "./repository-git.js";
|
|
7
|
+
import { parseGitUrl, fetchGitRepository, sparseFetchGitRepository, probeGitRemote, probeGitHubHttp } from "./repository-git.js";
|
|
8
8
|
const LIMIT = 32 << 20;
|
|
9
9
|
const STATE_LIMIT = 16 << 20;
|
|
10
10
|
const STATE_READ_LIMIT = 64 << 20;
|
|
@@ -128,6 +128,7 @@ async function readResponse(response, limit) {
|
|
|
128
128
|
function createRepositoryManager({
|
|
129
129
|
fetchImpl = globalThis.fetch,
|
|
130
130
|
gitFetch,
|
|
131
|
+
sparseFetch = sparseFetchGitRepository,
|
|
131
132
|
probeRemote,
|
|
132
133
|
maxRepositoryBytes: rawMaxBytes = 2 * 1024 * 1024 * 1024,
|
|
133
134
|
maxTransientBytes: rawMaxTransientBytes,
|
|
@@ -793,22 +794,35 @@ function createRepositoryManager({
|
|
|
793
794
|
commit = res.commit;
|
|
794
795
|
skills = await scanSkillsFromDirectory(stageDir, repo.subdirectory, repo.name);
|
|
795
796
|
} else {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
797
|
+
let useArchive = false;
|
|
798
|
+
try {
|
|
799
|
+
const res = await sparseFetch(repo, stageDir, { maxBytes: maxRepositoryBytes, maxTransientBytes, timeoutMs: gitTimeoutMs });
|
|
800
|
+
commit = res.commit;
|
|
801
|
+
skills = await scanSkillsFromDirectory(stageDir, repo.subdirectory, repo.name);
|
|
802
|
+
} catch (caught) {
|
|
803
|
+
const error = caught;
|
|
804
|
+
if (error.code !== "error.repo.noGit" && error.code !== "error.repo.network") throw error;
|
|
805
|
+
await fs.rm(stageDir, { recursive: true, force: true }).catch(() => void 0);
|
|
806
|
+
useArchive = true;
|
|
807
|
+
}
|
|
808
|
+
if (useArchive) {
|
|
809
|
+
const bytes = await fetchBranch(repo);
|
|
810
|
+
const entries = decodeRepositoryArchive(bytes);
|
|
811
|
+
for (const [path, b] of Object.entries(entries)) {
|
|
812
|
+
if (path !== "SKILL.md" && !path.endsWith("/SKILL.md")) continue;
|
|
813
|
+
const directory = path === "SKILL.md" ? "" : path.slice(0, -9);
|
|
814
|
+
if (repo.subdirectory && directory !== repo.subdirectory && !directory.startsWith(`${repo.subdirectory}/`)) continue;
|
|
815
|
+
if (b.length > 256 << 10 || skills.length >= 500 || skills.reduce((n, s) => n + Buffer.byteLength(s.body, "utf8"), 0) + b.length > 4 << 20) throw failure("\u4ED3\u5E93\u6280\u80FD\u6570\u91CF\u6216\u8BF4\u660E\u957F\u5EA6\u8D85\u9650");
|
|
816
|
+
const parsed = parseSkillDoc(Buffer.from(b).toString("utf8"));
|
|
817
|
+
const name = String(parsed.map.name || "");
|
|
818
|
+
const description = String(parsed.map.description || "");
|
|
819
|
+
const prefix = directory ? directory + "/" : "";
|
|
820
|
+
const files = fileIndex(Object.fromEntries(Object.entries(entries).filter(([p]) => p.startsWith(prefix)).map(([p, b2]) => [p.slice(prefix.length), b2])));
|
|
821
|
+
if (files.length > 1e3) throw failure("\u6280\u80FD\u6587\u4EF6\u6570\u91CF\u8D85\u9650");
|
|
822
|
+
skills.push({ path: directory, files, name: name || directory.split("/").pop() || repo.name, description, body: parsed.body.trim(), documentHash: hash(b), valid: parsed.hasFrontmatter && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name) && !/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(name) && name.length <= 100 && !!description });
|
|
823
|
+
}
|
|
824
|
+
commit = await cacheArchive(repo, bytes);
|
|
810
825
|
}
|
|
811
|
-
commit = await cacheArchive(repo, bytes);
|
|
812
826
|
}
|
|
813
827
|
} catch (caught) {
|
|
814
828
|
const error = caught;
|
package/lib/repository-git.js
CHANGED
|
@@ -195,8 +195,14 @@ function runGit(args, cwd, optionsOrTimeout, legacyGitBinary = "git") {
|
|
|
195
195
|
"core.symlinks=false",
|
|
196
196
|
"-c",
|
|
197
197
|
"core.autocrlf=false",
|
|
198
|
+
// 技能目录层级深时,Windows 默认 260 字符上限会让检出直接失败。
|
|
199
|
+
"-c",
|
|
200
|
+
"core.longpaths=true",
|
|
198
201
|
"-c",
|
|
199
202
|
"advice.detachedHead=false",
|
|
203
|
+
// 只支持公开仓库:禁用凭据助手,避免 Git Credential Manager 弹出账号选择窗口并卡住插件进程。
|
|
204
|
+
"-c",
|
|
205
|
+
"credential.helper=",
|
|
200
206
|
"-c",
|
|
201
207
|
"http.lowSpeedLimit=1000",
|
|
202
208
|
"-c",
|
|
@@ -208,16 +214,23 @@ function runGit(args, cwd, optionsOrTimeout, legacyGitBinary = "git") {
|
|
|
208
214
|
env: {
|
|
209
215
|
...process.env,
|
|
210
216
|
GIT_TERMINAL_PROMPT: "0",
|
|
217
|
+
GCM_INTERACTIVE: "never",
|
|
218
|
+
GIT_ASKPASS: "echo",
|
|
211
219
|
LC_ALL: "C",
|
|
212
220
|
LANGUAGE: "C"
|
|
213
221
|
},
|
|
214
|
-
|
|
222
|
+
// 只在确实要写规则时才接管 stdin,其余调用保持 ignore。
|
|
223
|
+
stdio: options.input === void 0 ? ["ignore", "pipe", "pipe"] : ["pipe", "pipe", "pipe"],
|
|
215
224
|
detached: process.platform !== "win32"
|
|
216
225
|
}
|
|
217
226
|
);
|
|
218
227
|
let stdout = "";
|
|
219
228
|
let stderr = "";
|
|
220
229
|
let killed = false;
|
|
230
|
+
if (options.input !== void 0 && child.stdin) {
|
|
231
|
+
child.stdin.on("error", () => void 0);
|
|
232
|
+
child.stdin.end(options.input);
|
|
233
|
+
}
|
|
221
234
|
child.stdout.on("data", (chunk) => {
|
|
222
235
|
stdout += chunk;
|
|
223
236
|
});
|
|
@@ -343,10 +356,117 @@ async function fetchGitRepository(repo, destinationDir, options = {}) {
|
|
|
343
356
|
throw err;
|
|
344
357
|
}
|
|
345
358
|
}
|
|
359
|
+
const SKILL_DOC_SUFFIX = "/SKILL.md";
|
|
360
|
+
const EMPTY_CHECKOUT_PATTERN = "/.dsh-skills-manager-empty/**";
|
|
361
|
+
function checkoutPatternFor(directory) {
|
|
362
|
+
return `/${directory.replace(/[[\]]/g, "\\$&")}/**`;
|
|
363
|
+
}
|
|
364
|
+
function skillCheckoutPatterns(paths, subdirectory = "") {
|
|
365
|
+
const collected = [];
|
|
366
|
+
for (const path of paths) {
|
|
367
|
+
if (typeof path !== "string" || !path) continue;
|
|
368
|
+
if (path === "SKILL.md") return null;
|
|
369
|
+
if (!path.endsWith(SKILL_DOC_SUFFIX)) continue;
|
|
370
|
+
const directory = path.slice(0, -SKILL_DOC_SUFFIX.length);
|
|
371
|
+
if (!directory) continue;
|
|
372
|
+
if (subdirectory && directory !== subdirectory && !directory.startsWith(`${subdirectory}/`)) continue;
|
|
373
|
+
safePath(directory);
|
|
374
|
+
collected.push(directory);
|
|
375
|
+
}
|
|
376
|
+
if (!collected.length) return [EMPTY_CHECKOUT_PATTERN];
|
|
377
|
+
const directories = [...new Set(collected)].sort();
|
|
378
|
+
return directories.filter((directory) => !directories.some((other) => other !== directory && directory.startsWith(`${other}/`))).map(checkoutPatternFor);
|
|
379
|
+
}
|
|
380
|
+
async function listTreePaths(destinationDir, revision, options) {
|
|
381
|
+
const { stdout } = await runGit(["ls-tree", "-r", "-z", "--name-only", "--", revision], destinationDir, options);
|
|
382
|
+
return stdout.split("\0").filter(Boolean);
|
|
383
|
+
}
|
|
384
|
+
async function clearSparseCheckout(destinationDir, options) {
|
|
385
|
+
for (const key of ["core.sparseCheckout", "core.sparseCheckoutCone"]) {
|
|
386
|
+
await runGit(["config", "--unset-all", key], destinationDir, options).catch(() => void 0);
|
|
387
|
+
}
|
|
388
|
+
await fs.rm(join(destinationDir, ".git", "info", "sparse-checkout"), { force: true }).catch(() => void 0);
|
|
389
|
+
}
|
|
390
|
+
async function sparseFetchGitRepository(repo, destinationDir, options = {}) {
|
|
391
|
+
const maxBytes = options.maxBytes ?? 2 * 1024 * 1024 * 1024;
|
|
392
|
+
const maxTransientBytes = options.maxTransientBytes ?? maxBytes * 2.5;
|
|
393
|
+
const timeoutMs = options.timeoutMs ?? 9e5;
|
|
394
|
+
const deadline = Date.now() + timeoutMs;
|
|
395
|
+
const gitBinary = options.gitBinary || "git";
|
|
396
|
+
const remainingTimeout = () => {
|
|
397
|
+
const remain = deadline - Date.now();
|
|
398
|
+
if (remain <= 0) throw failure("Git \u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5", "error.repo.network");
|
|
399
|
+
return { timeoutMs: remain, gitBinary };
|
|
400
|
+
};
|
|
401
|
+
const repoUrl = repo.url || `https://github.com/${repo.owner}/${repo.name}.git`;
|
|
402
|
+
const ref = repo.ref && repo.ref !== "HEAD" ? repo.ref : "HEAD";
|
|
403
|
+
await fs.mkdir(destinationDir, { recursive: true });
|
|
404
|
+
try {
|
|
405
|
+
await runGit(["init", "-q"], destinationDir, remainingTimeout());
|
|
406
|
+
await runGit(["remote", "add", "origin", repoUrl], destinationDir, remainingTimeout());
|
|
407
|
+
const refsToTry = ref === "HEAD" ? ["HEAD"] : [`refs/heads/${ref}`, `refs/tags/${ref}`, ref];
|
|
408
|
+
let fetched = false;
|
|
409
|
+
let lastError;
|
|
410
|
+
for (const r of refsToTry) {
|
|
411
|
+
try {
|
|
412
|
+
await runGit(["fetch", "--progress", "--depth", "1", "--no-tags", "--filter=blob:none", "--", "origin", r], destinationDir, remainingTimeout());
|
|
413
|
+
fetched = true;
|
|
414
|
+
break;
|
|
415
|
+
} catch (err) {
|
|
416
|
+
lastError = err;
|
|
417
|
+
try {
|
|
418
|
+
await runGit(["fetch", "--progress", "--depth", "1", "--no-tags", "--", "origin", r], destinationDir, remainingTimeout());
|
|
419
|
+
fetched = true;
|
|
420
|
+
break;
|
|
421
|
+
} catch (err2) {
|
|
422
|
+
lastError = err2;
|
|
423
|
+
if (r === refsToTry[refsToTry.length - 1]) {
|
|
424
|
+
try {
|
|
425
|
+
await runGit(["fetch", "--progress", "--no-tags", "--", "origin", r], destinationDir, remainingTimeout());
|
|
426
|
+
fetched = true;
|
|
427
|
+
} catch {
|
|
428
|
+
throw err2;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (!fetched) throw lastError || failure("Git \u5206\u652F\u6216\u6807\u7B7E\u4E0D\u5B58\u5728", "error.repo.network");
|
|
435
|
+
const { stdout: commitOut } = await runGit(["rev-parse", "FETCH_HEAD^{commit}"], destinationDir, remainingTimeout());
|
|
436
|
+
const commit = commitOut.trim();
|
|
437
|
+
const patterns = skillCheckoutPatterns(await listTreePaths(destinationDir, "FETCH_HEAD", remainingTimeout()), repo.subdirectory);
|
|
438
|
+
if (patterns) {
|
|
439
|
+
try {
|
|
440
|
+
await runGit(["sparse-checkout", "set", "--no-cone", "--stdin"], destinationDir, { ...remainingTimeout(), input: `${patterns.join("\n")}
|
|
441
|
+
` });
|
|
442
|
+
} catch (err) {
|
|
443
|
+
const code = err?.code;
|
|
444
|
+
if (code === "error.repo.noGit" || code === "error.repo.network") throw err;
|
|
445
|
+
await clearSparseCheckout(destinationDir, remainingTimeout());
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
await runGit(["checkout", "-q", "FETCH_HEAD"], destinationDir, remainingTimeout());
|
|
449
|
+
const rawSize = await getDirectorySize(destinationDir);
|
|
450
|
+
if (rawSize > maxTransientBytes) {
|
|
451
|
+
throw failure("\u4ED3\u5E93\u4E34\u65F6\u4E0B\u8F7D\u4F53\u79EF\u8FC7\u5927\uFF08\u5305\u542B Git \u5386\u53F2\u5BF9\u8C61\uFF09", "error.repo.transientTooLarge", { limit: maxTransientBytes, actual: rawSize });
|
|
452
|
+
}
|
|
453
|
+
await rmWithRetry(join(destinationDir, ".git"));
|
|
454
|
+
const totalSize = await getDirectorySize(destinationDir);
|
|
455
|
+
if (totalSize > maxBytes) {
|
|
456
|
+
throw failure("\u4ED3\u5E93\u8D85\u8FC7\u5927\u5C0F\u9650\u5236", "error.repo.tooLarge", { limit: maxBytes, actual: totalSize });
|
|
457
|
+
}
|
|
458
|
+
return { commit };
|
|
459
|
+
} catch (err) {
|
|
460
|
+
await rmWithRetry(destinationDir).catch(() => void 0);
|
|
461
|
+
throw err;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
346
464
|
export {
|
|
347
465
|
fetchGitRepository,
|
|
348
466
|
getDirectorySize,
|
|
349
467
|
parseGitUrl,
|
|
350
468
|
probeGitHubHttp,
|
|
351
|
-
probeGitRemote
|
|
469
|
+
probeGitRemote,
|
|
470
|
+
skillCheckoutPatterns,
|
|
471
|
+
sparseFetchGitRepository
|
|
352
472
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@michengai/dsh-skills-manager",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.34",
|
|
4
4
|
"description": "DSH Skills Manager — 在 DeepSeek Harness 中统一加载并安全管理本机 Agent Skills · Load and safely manage local Agent Skills in DSH",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|