🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

自助 Git 服务 weibaohui/dsh-git-server

DeepSeek Harnessv0.2.4
weibaohui38 天前有提交已提供中文文档扫描:含敏感能力

@weibaohui/dsh-git-server

30.6/100
综合分
1
GitHub 星标
466
npm 周下载
—/5
用户评分

数据截至 2026/9/29 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add @weibaohui/dsh-git-server
npm 源 · @weibaohui/dsh-git-server 的发布者账号已校验与本仓库一致

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么[目录]—
dsh 原生插件 · dev
暂无时间戳·判据 →
装得上吗[实装验证]—
曾在 dsh 0.2.1-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.2-rc.1,本站尚未在新版下复验
L5 · 运行时冒烟等级说明 →
数据截至 2026/10/28·判据 →
安全吗[自动扫描]—
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
数据截至 2026/11/06·判据 →
还在维护吗[仓库]—
更新放缓:最近一次提交在 38 天前
暂无时间戳·判据 →
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →

可信度证据

安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包@weibaohui/dsh-git-server @ 0.2.4
✓Node 引擎要求 >=22.5 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/14 00:36:05

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件写入剪贴板(不计风险)读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 116 处,展示前 102 条)
  • 读取本地文件
    src/index.js:127
    try { pwFileCache = fs.readFileSync(passwordFile(), 'utf8').trim() } catch { pwFileCache = '' }
  • 写入本地文件
    src/index.js:132
    fs.mkdirSync(path.dirname(passwordFile()), { recursive: true })
  • 写入本地文件
    src/index.js:133
    fs.writeFileSync(passwordFile(), pw + '\n', { mode: 0o600 })
  • 发起网络请求
    src/index.js:182
    const res = await fetch(`http://127.0.0.1:${cfg.port}/api/v1${path}`, {
  • 发起网络请求
    src/index.js:204
    const res = await fetch(`http://127.0.0.1:${cfg.port}/api/v1/users/root/tokens`, {
  • 发起网络请求
    src/index.js:218
    const res = await fetch(`http://127.0.0.1:${cfg.port}/api/v1${path}`, {
  • 读取本地文件
    src/index.js:231
    try { return fs.readFileSync(impersonateFile(), 'utf8').trim() } catch { return '' }
  • 写入本地文件
    src/index.js:235
    fs.mkdirSync(path.dirname(impersonateFile()), { recursive: true })
  • 写入本地文件
    src/index.js:236
    fs.writeFileSync(impersonateFile(), v + '\n', { mode: 0o600 })
  • 读取本地文件
    src/index.js:262
    try { const st = fs.statSync(file); stamp = `${st.mtimeMs}:${st.size}` } catch { return null }
  • 读取本地文件
    src/index.js:265
    umSessionsCache = JSON.parse(fs.readFileSync(file, 'utf8')).tokens || {}
  • 读取本地文件
    src/index.js:277
    try { fs.statSync(mod); return 'ok' } catch { return 'missing' }
  • 发起网络请求
    src/index.js:545
    const res2 = await fetch(`http://127.0.0.1:${cfg.port}/api/v1/repos/${owner}/${repo}/raw/${encodeURIComponent(q('ref'))}/${q('path').split('/').map(en
  • 发起网络请求
    src/index.js:641
    const upstream = await fetch(`http://127.0.0.1:${cfg.port}${kernelPath}`, {
  • 发起网络请求
    client/bundle.js:260
    const res = await fetch(API + path, {
  • 发起网络请求
    client/bundle.js:409
    fetch(API + '/status').then((r) => r.json()).then((st) => {
  • 写入剪贴板
    client/bundle.js:493
    try { navigator.clipboard.writeText(cloneUrl) } catch {}
  • 写入剪贴板
    client/bundle.js:650
    try { navigator.clipboard.writeText(link) } catch {}
  • 写入剪贴板
    client/bundle.js:732
    const copyBtn = (text) => h('button', { className: 'dgs-btn ghost', style: { padding: '2px 10px', fontSize: 12 }, onClick: () => { try { navigator.cli
  • 发起网络请求
    client/bundle.js:2720
    fetch(API + '/status').then((r) => r.json()).then((d) => {
  • 发起网络请求
    client/bundle.js:2728
    fetch(API + '/status').then((r) => r.json()).then((d) => { setStatus(d); setUnreachable(false) }).catch(() => setUnreachable(true))
  • 发起网络请求
    client/bundle.js:2735
    const res = await fetch(API + '/settings', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form) })
  • 发起网络请求
    client/bundle.js:2873
    fetch(API + '/me').then((r) => r.json()).then((d) => {
  • 读取本地文件
    src/runner.js:97
    const st = fs.statSync(path.join(SERVER_DIR, 'dist', 'index.js'))
  • 写入本地文件
    src/runner.js:108
    fs.mkdirSync(customDir, { recursive: true })
  • 写入本地文件
    src/runner.js:109
    fs.mkdirSync(path.join(cfg.dataDir, 'repositories'), { recursive: true })
  • 写入本地文件
    src/runner.js:110
    fs.mkdirSync(path.join(cfg.dataDir, 'log'), { recursive: true })
  • 读取本地文件
    src/runner.js:113
    try { secret = fs.readFileSync(secretFile, 'utf8').trim() } catch {}
  • 写入本地文件
    src/runner.js:116
    fs.writeFileSync(secretFile, secret + '\n', { mode: 0o600 })
  • 读取本地文件
    src/runner.js:154
    if (fs.readFileSync(file, 'utf8') === ini) return file
  • 写入本地文件
    src/runner.js:156
    fs.writeFileSync(file, ini, { mode: 0o600 })
  • 执行外部命令来自配置/变量
    src/runner.js:184
    const child = spawn(npm, ['install', '--omit=dev', '--no-audit', '--no-fund'], {
  • 读取本地文件
    src/runner.js:236
    try { fs.statSync(c); return c } catch {}
  • 读取本地文件
    src/runner.js:244
    try { return fs.readFileSync(file, 'utf8').trim() } catch {}
  • 写入本地文件
    src/runner.js:246
    fs.mkdirSync(path.dirname(file), { recursive: true })
  • 读取本地文件
    src/runner.js:277
    if (!fs.existsSync(entry)) throw new Error(`vendor/ts-gogs 构建产物缺失: ${entry}`)
  • 执行外部命令来自配置/变量
    src/runner.js:291
    const child = spawn(process.execPath, spawnArgs, { cwd: SERVER_DIR, env, stdio: ['ignore', 'pipe', 'pipe'] })
  • 发起网络请求
    src/runner.js:311
    const res = await fetch(`http://${cfg.host === '0.0.0.0' || cfg.host === '::' ? '127.0.0.1' : cfg.host}:${cfg.port}/`, { redirect: 'manual' })
  • 发起网络请求
    client/index.js:250
    const res = await fetch(API + path, {
  • 发起网络请求
    client/index.js:399
    fetch(API + '/status').then((r) => r.json()).then((st) => {
  • 写入剪贴板
    client/index.js:483
    try { navigator.clipboard.writeText(cloneUrl) } catch {}
  • 写入剪贴板
    client/index.js:640
    try { navigator.clipboard.writeText(link) } catch {}
  • 写入剪贴板
    client/index.js:722
    const copyBtn = (text) => h('button', { className: 'dgs-btn ghost', style: { padding: '2px 10px', fontSize: 12 }, onClick: () => { try { navigator.cli
  • 发起网络请求
    client/index.js:2710
    fetch(API + '/status').then((r) => r.json()).then((d) => {
  • 发起网络请求
    client/index.js:2718
    fetch(API + '/status').then((r) => r.json()).then((d) => { setStatus(d); setUnreachable(false) }).catch(() => setUnreachable(true))
  • 发起网络请求
    client/index.js:2725
    const res = await fetch(API + '/settings', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form) })
  • 发起网络请求
    client/index.js:2863
    fetch(API + '/me').then((r) => r.json()).then((d) => {
  • 读取本地文件
    server/dist/conf.js:178
    const defaultIni = fs.readFileSync(path.join(workDir, 'vendored-conf', 'app.ini'), 'utf8');
  • 读取本地文件
    server/dist/conf.js:181
    if (fs.existsSync(this.customConf)) {
  • 读取本地文件
    server/dist/conf.js:182
    const custom = parseIni(fs.readFileSync(this.customConf, 'utf8'));
  • 写入本地文件
    server/dist/dshapi.js:218
    fs.mkdirSync(patchDir, { recursive: true });
  • 写入本地文件
    server/dist/dshapi.js:219
    fs.writeFileSync(path.join(patchDir, `${index}.patch`), patch);
  • 写入本地文件
    server/dist/dshapi.js:470
    fs.mkdirSync(tmp, { recursive: true });
  • 读取本地文件
    server/dist/dshapi.js:473
    if (!fs.existsSync(path.join(tmp, fileName))) {
  • 删除本地文件
    server/dist/dshapi.js:486
    fs.rmSync(tmp, { recursive: true, force: true });
  • 写入本地文件
    server/dist/dshapi.js:506
    fs.mkdirSync(tmp, { recursive: true });
  • 写入本地文件
    server/dist/dshapi.js:510
    fs.mkdirSync(path.dirname(fp), { recursive: true });
  • 读取本地文件
    server/dist/dshapi.js:511
    const exists = fs.existsSync(fp);
  • 写入本地文件
    server/dist/dshapi.js:516
    fs.writeFileSync(fp, content);
  • 删除本地文件
    server/dist/dshapi.js:526
    fs.rmSync(tmp, { recursive: true, force: true });
  • 读取本地文件
    server/dist/dshapi.js:550
    const buf = fs.readFileSync(dst);
  • 删除本地文件
    server/dist/dshapi.js:562
    fs.rmSync(dst, { force: true });
  • 读取本地文件
    server/dist/dshapi.js:856
    if (fs.existsSync(dir))
  • 删除本地文件
    server/dist/dshapi.js:857
    fs.rmSync(dir, { recursive: true, force: true });
  • 删除本地文件
    server/dist/dshapi.js:1285
    fs.rmSync(tmpDir, { recursive: true, force: true });
  • 读取本地文件
    server/dist/dshapi.js:1309
    if (!fs.existsSync(dir)) {
  • 读取本地文件
    server/dist/dshapi.js:1367
    if (!fs.existsSync(dir)) {
  • 删除本地文件
    server/dist/dshapi.js:1764
    fs.rmSync(path.join(attachDir(), a.uuid), { force: true });
  • 写入本地文件
    server/dist/index.js:15
    fs.mkdirSync(customDir, { recursive: true });
  • 执行外部命令固定命令
    server/dist/index.js:22
    conf.buildCommit = execSync('git rev-parse --short HEAD', { cwd: workDir }).toString().trim();
  • 读取密钥环境变量
    server/dist/index.js:68
    const adminPass = process.env.DSH_ADMIN_PASSWORD || '';
  • 写入本地文件
    server/dist/lfsx.js:96
    fs.mkdirSync(path.dirname(dst), { recursive: true });
  • 读取本地文件
    server/dist/lfsx.js:199
    if (!fs.existsSync(file)) {
  • 读取本地文件
    server/dist/lfsx.js:204
    res.setHeader('Content-Length', String(fs.statSync(file).size));
  • 读取本地文件
    server/dist/lfsx.js:205
    fs.createReadStream(file).pipe(res);
  • 写入本地文件
    server/dist/lfsx.js:226
    fs.writeFileSync(dst, content);
  • 读取本地文件
    server/dist/mirror.js:11
    const cfg = fs.readFileSync(path.join(repoDir, 'config'), 'utf8');
  • 读取本地文件
    server/dist/mirror.js:133
    if (fs.existsSync(wikiDir)) {
  • 读取本地文件
    server/dist/repox.js:33
    if (!fs.existsSync(patchFile))
  • 写入本地文件
    server/dist/repox.js:36
    fs.mkdirSync(tmpDir, { recursive: true });
  • 删除本地文件
    server/dist/repox.js:52
    fs.rmSync(tmpDir, { recursive: true, force: true });
  • 写入本地文件
    server/dist/repox.js:60
    fs.mkdirSync(tmpDir, { recursive: true });
  • 读取本地文件
    server/dist/repox.js:72
    if (fs.existsSync(target))
  • 删除本地文件
    server/dist/repox.js:73
    fs.unlinkSync(target);
  • 读取本地文件
    server/dist/repox.js:75
    if (fs.existsSync(symlink))
  • 删除本地文件
    server/dist/repox.js:76
    fs.unlinkSync(symlink);
  • 读取本地文件
    server/dist/repox.js:81
    if (fs.existsSync(oldPath))
  • 删除本地文件
    server/dist/repox.js:82
    fs.unlinkSync(oldPath);
  • 读取本地文件
    server/dist/repox.js:84
    if (!isDelete && fs.existsSync(target) && oldTitle === null) {
  • 写入本地文件
    server/dist/repox.js:87
    fs.writeFileSync(target, content);
  • 删除本地文件
    server/dist/repox.js:96
    fs.rmSync(tmpDir, { recursive: true, force: true });
  • 读取本地文件
    server/dist/server.js:34
    const stat = fs.statSync(file);
  • 读取本地文件
    server/dist/server.js:43
    fs.createReadStream(file).pipe(res);
  • 读取本地文件
    server/dist/server.js:68
    if (fs.existsSync(customDir)) {
  • 读取本地文件
    server/dist/server.js:168
    if (fs.existsSync(f)) {
  • 读取本地文件
    server/dist/server.js:235
    if (fs.existsSync(cached)) {
  • 读取本地文件
    server/dist/server.js:238
    fs.createReadStream(cached).pipe(res);
  • 写入本地文件
    server/dist/server.js:248
    fs.mkdirSync(conf.avatarUploadPath, { recursive: true });
  • 写入本地文件
    server/dist/server.js:249
    fs.writeFileSync(cached, buf);
  • 写入本地文件
    server/dist/server.js:255
    fs.mkdirSync(conf.avatarUploadPath, { recursive: true });
  • 写入本地文件
    server/dist/server.js:256
    fs.writeFileSync(failMark, '');
  • 发起网络请求
    server/dist/webhook.js:109
    const req = mod.request({
扫描于 2026/11/6(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

社区实装还没有人打卡 · 装过的第一个就是你
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
v0.2.52026-10-29
API 基址改由 document.baseURI 推导(与宿主官方客户端同口径):子路径反代(如 /dsh-git-server/api 会丢前缀打到站点根而 404;根路径部署行为不变。dsh-sync#14 同款修复,全插件批次落地。
v0.1.22026-09-23
HTTP API 路由接入宿主 connection 信任栅栏(Host/Origin 校验 + 浏览器认证),防止本机任意网页跨站调用。同源问题修复见 dsh-tasks#7 / skills-management#9。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
展开完整文档(README 剩余 1 节)
@weibaohui/dsh-git-server

DSH plugin
npm version

Git 服务器插件:把一套完整的自助 Git 服务装进 dsh——HTTP clone/push、工单、PR、wiki、发版、标签、里程碑、webhook 全功能,网页侧全程是 dsh 原生界面,账号可复用 user-management。

核心功能

- 完整自助 Git 服务:HTTP clone/push 开箱即用,仓库/工单/PR/wiki/发版/标签/里程碑一条龙,崩溃自动拉起
- dsh 原生管理页:侧栏底部「Git」进全屏页——建仓(或从 URL 导入/镜像)删仓/克隆地址,仓库浏览多标签(文件树+语法高亮、提交历史、分支、工单、PR、Wiki、发版、标签、里程碑、设置);React + dsh 主题(亮暗自动跟随)、zh/en 双语
- 协作闭环:通知中心(评论/指派/@提及未读 + 侧栏角标)、跨仓库「我的工单/PR」聚合、PR 评审(通过/请求修改)、fixes #N push 自动关单、提交与工单互相跳转
- 工程能力:分支保护(禁删/禁强推)、协作者只读/可写/管理三档、发版二进制附件、仓库内代码搜索、webhook 测试投递与投递记录、文件永久链接深链
- 账号打通:账户来源 = user-management(必装)——git 凭据与页面登录均走 user-management 校验,免另记一套账号;网页注册关闭,账号只由 dsh 管理
- 一键启停:启用/停用/端口/数据目录全在设置页,改完即生效
- 局域网 git:默认监听 0.0.0.0,局域网内机器可直接 git clone/push
- 数据自持:数据目录 = 仓库 + 数据库 + 日志,备份整个目录即备份全部

安装

本插件依赖 user-management 提供登录与账户,必须先装:

dsh plugin --profile web add @weibaohui/user-management -w
dsh plugin --profile web add @weibaohui/dsh-git-server -w

装完重启 dsh web 即生效。

使用

1. 打开 Web UI → 设置 → Git 服务器——默认已启用(监听 0.0.0.0:3400,无需勾选;要停用在设置页取消勾选),可在此改端口/数据目录/查看 root 密码
2. 侧栏底部点「Git」进全屏管理页:新建仓库(或导入已有),照常开发
3. git clone/push 走子进程端口,凭据用 user-management 的用户名密码:

git clone http://127.0.0.1:3400/root/drill-repo.git
cd drill-repo && git checkout -b feature && ... && git push origin feature

4. 仓库浏览、工单、PR、wiki、发版、标签、里程碑等日常操作全部在原生管理页完成

联系我 :飞书群

link

版本兼容性

本插件与 DeepSeek Harness(@deepseek-ai/dsh)的版本对应关系:

| 插件版本 | 适配 dsh 版本 | 备注 |
| 0.2.3 | 0.1.7-rc.2 | 当前版本;修复 Config 降级导出 null——宿主 settings schema() 不排除 null,会炸 settings.describe() 拖垮整份设置文档(同 dsh-continue#4) |
| 0.2.0 | 0.1.7-rc.2 | 已在 @deepseek-ai/dsh@0.1.7-rc.2 下验证运行 |
| 0.1.3 | 0.1.7-rc.2 | 已在 @deepseek-ai/dsh@0.1.7-rc.2 下验证运行 |

发版约定:每次发布新版本时,请在上表追加一行,记录该插件版本实际验证所用的 @deepseek-ai/dsh 版本。package.json 的 engines.dsh 声明最低支持版本;本表记录实际验证版本,二者配合使用。

功能巡览

逐页截图(点击展开)

主页

| 仓库列表 | 从 URL 导入/镜像 |
| 主页 | 导入 |

代码浏览

| 文件树+README | 语法高亮 | Blame | 文件历史 | 空仓库推送引导 |
| 文件树 | 高亮 | blame | 历史 | 空仓库 |

| 提交历史 | 提交 diff | 仓库内代码搜索 |
| 提交 | diff | 搜索 |

| 分支概况 | 分支与标签 |
| 分支 | 分支标签 |

工单 / PR / 协作

| 工单列表 | 工单详情 | 新工单 |
| 工单 | 工单详情 | 新工单 |

| 标签管理 | 里程碑 | PR 列表 |
| 标签 | 里程碑 | PR |

| PR 评审与合并 | 已合并 PR(评审记录) |
| PR评审 | PR已合并 |

| 通知中心 | 我的工单聚合 | 星标/复刻弹层 |
| 通知 | 我的工单 | 星标 |

Wiki / 发版

| Wiki 页面 | 发版(含二进制附件) |
| wiki | 发版 |

仓库设置

| 协作者(三档权限) | 分支保护 | Webhook 与投递记录 | 镜像同步 | 危险区域 |
| 协作者 | 分支保护 | webhook | 镜像 | 危险区域 |

全局页面

| 探索 | 组织 | 用户主页 | 管理面板 |
| 探索 | 组织 | 用户 | 管理 |
预览分享卡片长什么样 ↓weibaohui/dsh-git-server
分享 · DeepSeek Harness Hub
weibaohui/dsh-git-server

@weibaohui/dsh-git-server

这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。
认领条目 →
回到顶部