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

Git 凭据保险箱 revive/dsh-git-credentials

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

DeepSeek Harness 的独立外挂插件:管理 GitLab、GitHub、Gitee、Gitea 与…

36.2/100
综合分
3
GitHub 星标
415
npm 周下载
—/5
用户评分

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · market
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 0 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

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

✓npm 包dsh-git-credentials @ 0.6.1
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/31 10:23:27

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

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

读写/删除本地文件发起外部网络请求
查看扫描证据(共 60 处,展示前 47 条)
  • 发起网络请求
    lib/index.js:403
    response = await fetch(url, {
  • 发起网络请求
    lib/index.js:423
    response = await fetch(url, {
  • 发起网络请求
    lib/index.js:448
    response = await fetch(url, {
  • 发起网络请求
    lib/index.js:474
    response = await fetch(url, {
  • 发起网络请求
    lib/index.js:878
    response = await fetch(url, {
  • 发起网络请求
    lib/index.js:910
    response = await fetch(url, {
  • 读取本地文件
    lib/index.js:2506
    if (!existsSync(this.paths.dataPath)) return {
  • 读取本地文件
    lib/index.js:2513
    file = JSON.parse(readFileSync(this.paths.dataPath, "utf8"));
  • 写入本地文件
    lib/index.js:2550
    writeFileSync(tmp, JSON.stringify(file), { mode: 384 });
  • 写入本地文件
    lib/index.js:2551
    renameSync(tmp, this.paths.dataPath);
  • 读取本地文件
    lib/index.js:2560
    if (!existsSync(this.paths.keyPath)) throw new Error(`gitlab-plugin: key file ${this.paths.keyPath} is missing — the data file cannot be decrypted; ke
  • 读取本地文件
    lib/index.js:2561
    return readFileSync(this.paths.keyPath);
  • 读取本地文件
    lib/index.js:2565
    if (existsSync(this.paths.keyPath)) return this.readKey();
  • 写入本地文件
    lib/index.js:2567
    mkdirSync(dirname(this.paths.keyPath), {
  • 写入本地文件
    lib/index.js:2571
    writeFileSync(this.paths.keyPath, key, { mode: 384 });
  • 发起网络请求
    lib/client.js:18
    const response = await fetch(path);
  • 发起网络请求
    lib/client.js:24
    const response = await fetch(path, {
  • 发起网络请求
    src/bitbucket.ts:192
    response = await fetch(url, {
  • 发起网络请求
    src/bitbucket.ts:588
    response = await fetch(url, {
  • 发起网络请求
    src/bitbucket.ts:633
    response = await fetch(url, {
  • 发起网络请求
    src/client/admin-api.ts:42
    const response = await fetch(path)
  • 发起网络请求
    src/client/admin-api.ts:49
    const response = await fetch(path, {
  • 发起网络请求
    src/gitea.ts:582
    response = await fetch(url, {
  • 发起网络请求
    src/gitea.ts:605
    response = await fetch(url, {
  • 发起网络请求
    src/gitea.ts:635
    response = await fetch(url, {
  • 发起网络请求
    src/gitea.ts:680
    response = await fetch(url, {
  • 发起网络请求
    src/gitee.ts:634
    response = await fetch(url, {
  • 发起网络请求
    src/gitee.ts:677
    response = await fetch(url, {
  • 发起网络请求
    src/gitee.ts:718
    response = await fetch(url, {
  • 发起网络请求
    src/gitee.ts:769
    response = await fetch(url, {
  • 发起网络请求
    src/github.ts:604
    response = await fetch(url, {
  • 发起网络请求
    src/github.ts:641
    response = await fetch(url, {
  • 发起网络请求
    src/github.ts:671
    response = await fetch(url, {
  • 发起网络请求
    src/github.ts:716
    response = await fetch(url, {
  • 发起网络请求
    src/gitlab.ts:638
    response = await fetch(url, {
  • 发起网络请求
    src/gitlab.ts:661
    response = await fetch(url, {
  • 发起网络请求
    src/gitlab.ts:686
    response = await fetch(url, {
  • 发起网络请求
    src/gitlab.ts:717
    response = await fetch(url, {
  • 读取本地文件
    src/store.ts:88
    if (!existsSync(this.paths.dataPath)) return { sites: {}, tokens: {} }
  • 读取本地文件
    src/store.ts:92
    file = JSON.parse(readFileSync(this.paths.dataPath, 'utf8')) as EncryptedFile
  • 写入本地文件
    src/store.ts:140
    writeFileSync(tmp, JSON.stringify(file), { mode: 0o600 })
  • 写入本地文件
    src/store.ts:141
    renameSync(tmp, this.paths.dataPath)
  • 读取本地文件
    src/store.ts:152
    if (!existsSync(this.paths.keyPath)) {
  • 读取本地文件
    src/store.ts:157
    return readFileSync(this.paths.keyPath)
  • 读取本地文件
    src/store.ts:162
    if (existsSync(this.paths.keyPath)) return this.readKey()
  • 写入本地文件
    src/store.ts:164
    mkdirSync(dirname(this.paths.keyPath), { recursive: true, mode: 0o700 })
  • 写入本地文件
    src/store.ts:165
    writeFileSync(this.paths.keyPath, key, { mode: 0o600 })
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

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

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-tools@deepseek-ai/schemastery
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 静态加密存储凭据
  • 工具按需加载
  • 网页设置面板

DeepSeek Harness 插件:GitLab 和 GitHub API token 不进模型上下文——静态加密(AES-256-GCM),工具按需加载,带网页设置面板。

v0.6.12026-10-22
· 新增 · 站点表单的 token 字段直接链到所选服务商的 token 创建页:GitHub 的细粒度 PAT 页面(已预填 token 名称)、Gitee 和 Bitbucket 的固定页面,以及 GitLab 和 Gitea 的页面(由站点的 API 基础 URL 推导)——当该 URL 为空或无法解析时,链接会隐藏,而不是指向错误的主机。未配置 token 时,该字段还会说明平台的最低 scope/权限(GitHub 细粒度:Contents、Issues 和 Pull requests 读写;GitLab api;Gitee projects;Gitea repo;Bitbucket repositories、pull requests 和 issues 读写)。链接在本地生成,绝不携带 token。修复 #2。 --- 安装:下载上方的 dsh-git-credentials-0.6.1.tgz,运行 dsh plugin --profile add ./dsh-git-credentials-0.6.1.tgz,然后重启 GUI。同一版本发布到 npm 后,可用 dsh plugin --profile add dsh-git-credentials 从 npm 安装。
v0.6.02026-10-22
· 新增 · gitlab_projects create 接受 namespace 参数:组的完整路径(例如 pandax 或 team/sub)或数字 id,会通过 GET /groups/:id 解析并以 namespace_id 发送,因此项目可以建在组里,而不只是建在 token 所有者名下。省略该参数则保持原有行为,其他服务商仍建在 token 所有者名下(Bitbucket:站点 defaultProject 指定的工作区)。 · 变更 · GitLab 项目列表在 path 中返回完整路径(group/project),与其他所有服务商一致,也与 create 已返回的值一致,因此列表里的项目可以直接传给接受项目的工具。 --- 安装:下载上面的 dsh-git-credentials-0.6.0.tgz,运行 dsh plugin --profile add ./dsh-git-credentials-0.6.0.tgz,然后重启 GUI。同一版本发布到 npm 后,可用 dsh plugin --profile add dsh-git-credentials 安装。
v0.5.02026-10-09
· 新增 · 设置面板围绕两个清晰分开的区域重建:已配置的站点是卡片列表,每张卡片带站点信息和行操作;添加表单是独立卡片,只有一个保存按钮。 · 面板沿用 Harness 的界面语言:文案和导航标签通过客户端 locale 服务注册中英文,面板通过其区块注册声明的 locale 绑定 t 读取它们。 · 站点可为工具支持的所有服务商配置。管理端站点写入原先只接受 gitlab 和 github,导致 Gitee、Gitea、Bitbucket 站点即使存储层和工具层支持也被 HTTP 400 拒绝;现在写入接受全部五家服务商。 · 变更 · 一个站点只拥有一个 token。token 引用名已从存储、管理 API、工具和面板中移除:保存站点及其 token 是一次请求,轮换 token 就是同一次保存,删除站点即删除其 token,凭据既不会比站点存活更久,也不会被意外共享。旧版本写入的存储在读取时迁移——每个站点引用的值变成它自己的 token,没有站点引用的引用键被丢弃。 · 字段规则在写入前说明,且只陈述规则本身:站点 id 字符集和 http(s) API 根地址显示为简短的字段帮助,无效草稿用文字说明原因而不是正则表达式,并禁用保存,切换服务商只替换用户尚未输入的值。 · 面板样式参照旁边的 Host 页面:沿用其控件尺寸、焦点行为和列表节奏,与 Host 共享的只有 --dsw-* 主题 token。原先硬编码的颜色已移除,面板跟随浅色和深色主题。 · GitHub Releases 按本文件对应标签版本的章节生成,而不是固定通知;没有章节的版本回退到 GitHub 自动生成的说明。 --- 安装:下载上面的 dsh-git-credentials-0.5.0.tgz,运行 dsh plugin --profile add ./dsh-git-credentials-0.5.0.tgz,然后重启 GUI。同一版本发布到 npm 后,可用 dsh plugin --profile add dsh-git-credentials 安装。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
dsh-git-credentials English · 简体中文 DeepSeek Harness 的独立外挂插件:管理 GitLab、GitHub、Gitee、Gitea 与 Bitbucket 的 API token,token 值永不进入大模型的上下文。 模型工具只携带站点 id(如 corp),该站点自己的 token 在每次调用时才从插件的加密存储中解密一次,只出现在发出的 HTTP 请求头里。修改站点或轮换 token 后下一次调用立即生效,无需重启。 特性 - token 不进入模型上下文——工具参数、返回值、错误信息里只有业务数据(site、project、path 等) - 磁盘静态加密——AES-256-GCM 整体加密的数据文件 + 独立的 32 字节随机密钥文件(0600、原子写入) - 按 provider 限定工具——gitlab_ 只见 GitLab 站点,github_ 只见 GitHub 站点,gitee_ / gitea_ / bitbucket_ 同理;未配置的 site/token 响亮失败并列出合法值 - Web 设置面板——添加/编辑/删除站点、写入或清除 token 值;任何响应都不携带 token 值,只报配置状态 - 动态加载/卸载——运行中的 GUI 直接热挂载/热卸载,无需重启
展开完整文档(README 剩余 1 节)
dsh-git-credentials

English · 简体中文

DeepSeek Harness 的独立外挂插件:管理 GitLab、GitHub、Gitee、Gitea 与 Bitbucket 的 API token,token 值永不进入大模型的上下文。

模型工具只携带站点 id(如 corp),该站点自己的 token 在每次调用时才从插件的加密存储中解密一次,只出现在发出的 HTTP 请求头里。修改站点或轮换 token 后下一次调用立即生效,无需重启。

特性

- token 不进入模型上下文——工具参数、返回值、错误信息里只有业务数据(site、project、path 等)
- 磁盘静态加密——AES-256-GCM 整体加密的数据文件 + 独立的 32 字节随机密钥文件(0600、原子写入)
- 按 provider 限定工具——gitlab_ 只见 GitLab 站点,github_ 只见 GitHub 站点,gitee_ / gitea_ / bitbucket_ 同理;未配置的 site/token 响亮失败并列出合法值
- Web 设置面板——添加/编辑/删除站点、写入或清除 token 值;任何响应都不携带 token 值,只报配置状态
- 动态加载/卸载——运行中的 GUI 直接热挂载/热卸载,无需重启
- 即时生效——每次工具调用读一份解密快照,改动和轮换立即生效

为什么不用 MCP server?

GitHub 发布了官方 MCP server,harness 也原生支持 MCP 客户端——如果只需要 GitHub,接入官方 MCP server 是更主流的选择,本插件的 github_ 工具确实与它功能重叠。

本插件的价值在 MCP 覆盖不到的地方:

| | 官方 GitHub MCP | 本插件 |
| 覆盖平台 | 只有 GitHub(GitLab 有官方 server;Gitee / Gitea / Bitbucket 依赖第三方 server,质量与维护参差) | 一个加密存储、一个设置面板、一套工具管 GitLab、GitHub、Gitee、Gitea、Bitbucket——含自托管 Gitea / GitLab |
| token 处理 | 每台 server 环境变量明文配置,无管理界面 | AES-256-GCM 加密存储 + 一个站点一个 token + 设置页管理;token 值永不进入模型上下文 |
| 集成形态 | 多一层 MCP 代理进程 | 工具直接注册进 harness 工具注册表 |

单一托管平台 + 标准 token 处理,走 MCP 即可;多 forge(尤其是 Gitee、自托管 Gitea)、或想要加密存储 + 产品内管理页面时,用本插件。

安全模型

存储

- ~/.dsh/git-credentials.json — 数据文件,AES-256-GCM 整体加密(0600、原子写入)
- ~/.dsh/git-credentials.key — 32 字节随机密钥,独立文件存放(0600)

威胁模型

| 场景 | 是否防护 |
| 有人拷走/备份/同步数据文件 | ✅ 是——只有密文,没有密钥文件解不开 |
| 同 UID 进程(如 agent 的 bash/fs 工具)读取两个文件 | ❌ 否——密钥与数据同权限,与产品自身密钥处理同级("discretion, not a boundary") |
| 用户主动让模型去读文件 | ❌ 否——不在防护范围内,任何系统都拦不住 |

密钥文件丢失 = 数据不可解(解密失败会响亮报错并提示密钥路径);数据文件单独被拷走 = 安全。

安装

方式 A:安装 release tarball(推荐)

从 releases 页面 下载 dsh-git-credentials-<version>.tgz——tarball 自带构建好的浏览器 bundle,无需 harness 检出、无需构建——然后用 dsh CLI 装进 profile:

dsh plugin --profile <name> add ./dsh-git-credentials-0.4.0.tgz

首次使用会初始化 profile、pnpm 链接包,dsh 自动把插件追加进 profile 的 bundle 层。不 boot 先验证层:

dsh --profile <name> --dump-config # 找 "# == dsh-git-credentials"

安装 bundle 不会热挂载到运行中的 GUI:bundle 层在启动时组合(HMR 只热应用 patch 文件),dsh plugin add 之后必须重启 GUI 进程。重启后在 设置 → Git 凭据 即可看到插件分区。

方式 B:从源码检出安装

插件是纯外挂,产品代码零改动,~/.dsh 下只需两处:

1. 符号链接插件目录,让所有 profile 都能解析包名:

mkdir -p ~/.dsh/profiles/node_modules
ln -s /path/to/dsh-git-credentials ~/.dsh/profiles/node_modules/dsh-git-credentials

2. 在 home 层覆盖文件 ~/.dsh/cordis.patch.yml 中追加插件行(对 web/headless 等所有 profile 生效):

- insert:
- id: git-credentials
name: 'dsh-git-credentials'

HMR watcher 监控 home 层:加行 = 热挂载(运行中的 GUI 直接生效),删行 / disabled: true = 热卸载,改配置 = 热重配。卸载即删掉这两处。

浏览器半(lib/client.js)是构建产物——克隆后先构建(见开发);release tarball 已包含构建产物。

用法

在 设置 → Git 凭据 中管理站点与 token:

- 添加站点:表单在列表下方独立的「新增站点」卡片里,含站点 id、provider(GitLab / GitHub / Gitee / Gitea / Bitbucket)、API 地址(各 provider 默认值:https://api.github.com、https://gitee.com/api/v5、https://api.bitbucket.org/2.0;GitLab 与 Gitea 是自托管,需自己填地址,如 https://gitlab.example.com / https://gitea.example.com/api/v1)、该站点的 token 与默认项目(可选)。一个「保存」把站点与 token 一起写入;字段规则(站点 id 字符集、http(s) 地址)直接写在输入框下方,草稿不合法时说明问题并禁用「保存」,不必等服务器报错
- 每个已保存的站点:默认在自己的卡片里只读展示(provider、地址、默认项目、token 状态),操作为「编辑」与「删除站点」;编辑态显示各输入框及「保存 / 取消」,一次保存同时提交字段与新填的 token,清除 token 用 token 字段旁的「清除 token」;删除站点会连同它的 token 一起删除
- 一个站点一个 token:token 属于保存它的那个站点,既不会比站点活得更久,也不会被误共享
- token 帮助:token 字段旁有「获取 token ↗」直达对应平台的创建页——GitHub 的 fine-grained PAT 页(预填名称)、Gitee 与 Bitbucket 的固定页面、GitLab 与 Gitea 由站点 API 地址推导(地址为空或不合法时隐藏链接);token 未配置时字段还会写明该平台的最低 scope/权限。链接在本地生成,不携带 token
- 面板跟随 Harness 的界面语言:文案与导航标题都通过客户端 locale 服务注册了中文与英文
- 面板通过同源 /git-credentials-admin/ JSON 端点读写加密存储;任何响应都不携带 token 值
- 所有改动即时生效——每次工具调用读一份解密快照

工具

每个平台一个资源工具,action 参数选择操作。list 类 action 返回该平台的规范摘要形状(仓库:{id, path, name, webUrl, visibility};issue/PR:{number|iid, title, state, webUrl, authorName};文件:{path, ref, content, truncated})。get 完整读取单个 issue 或 MR/PR——在摘要之外附 body、labels、createdAt、updatedAt、bodyTruncated,MR/PR 另有 sourceBranch、targetBranch、draft;正文按配置的字节上限截断,被截断时会显式标记。comments 列出某个 issue/PR/MR 的评论,comment 发表评论;两者都返回 {id, body, authorName, createdAt, updatedAt, webUrl, bodyTruncated},正文同样按上限截断。写操作会真实修改远端——模型调用前应与用户确认。

| 工具 | action | 参数 |
| gitlab_projects | list、create | list: search?、membership?、perPage? · create: name、namespace?、description?、path?、visibility? |
| gitlab_file | read | project、path、ref? |
| gitlab_merge_requests | list、get、create、merge、close、comment、comments | project?、state?、perPage?、number、title、sourceBranch、targetBranch、body? |
| gitlab_issues | list、get、create、close、reopen、comment、comments | project?、state?、perPage?、number、title、body? |
| github_repos | list、create | list: search?、perPage? · create: name、description?、private? |
| github_file | read | project(owner/repo)、path、ref? |
| github_pull_requests | list、get、create、merge、close、comment、comments | project?、state?、perPage?、number、title、head、base、body? |
| github_issues | list、get、create、close、reopen、comment、comments | project?、state?、perPage?、number、title、body? |
| gitee_repos | list、create | list: search?、perPage? · create: name、description?、private? |
| gitee_file | read | project(owner/repo)、path、ref? |
| gitee_pull_requests | list、get、create、merge、close、comment、comments | project?、state?、perPage?、number、title、head、base、body? |
| gitee_issues | list、get、create、close、reopen、comment、comments | project?、state?、perPage?、number、title、body? |
| gitea_repos | list、create | list: search?、perPage? · create: name、description?、private? |
| gitea_file | read | project(owner/repo)、path、ref? |
| gitea_pull_requests | list、get、create、merge、close、comment、comments | project?、state?、perPage?、number、title、head、base、body? |
| gitea_issues | list、get、create、close、reopen、comment、comments | project?、state?、perPage?、number、title、body? |
| gitlab_releases | list、create、delete | project?、perPage?、tag(create 必需;GitLab 按 tag 删除)、name?、body?、draft?、prerelease? |
| github_releases | list、create、delete | project?、perPage?、tag、number(release id,delete 必需)、name?、body?、draft?、prerelease? |
| gitee_releases | list、create、delete | project?、perPage?、tag、number(release id,delete 必需)、name?、body?、draft?、prerelease? |
| gitea_releases | list、create、delete | project?、perPage?、tag、number(release id,delete 必需)、name?、body?、draft?、prerelease? |
| bitbucket_repos | list、create | list: search?、perPage? · create: name、description?、private? |
| bitbucket_file | read | project(workspace/repo)、path、ref? |
| bitbucket_pull_requests | list、get、create、merge、close、comment、comments | project?、state?、perPage?、number、title、head、base、body? |
| bitbucket_issues | list、get、create、close、reopen、comment、comments | project?、state?、perPage?、number、title、body? |

- action 默认读操作(list;file 为 read)——现有读调用不受影响
- number 是 issue/PR 编号(GitLab 为 iid);get / close / reopen / comment / comments / merge 必需
- Gitee 的 issue 编号是字符串(如 IKRZSO):gitee_issues 的 number 同时接受整数和这种字符串。Gitee 的 issue 创建/状态变更走 owner 级端点(客户端在 body 里带 repo)
- comments 与 comment 同时覆盖 issue 和 PR/MR:GitHub、Gitea 的 PR 普通评论走 issue-comments 端点;GitLab 读 notes 并过滤 system note;Gitee 读 issue 或 PR 评论(PR 创建评论的响应很稀疏,会回读列表补全);Bitbucket 跳过已删除的评论
- 所有客户端都保留站点 API 地址中的路径段(Gitee /api/v5、Gitea /api/v1、Bitbucket /2.0、GitHub Enterprise /api/v3)——不能用绝对端点路径去解析 base,否则会丢掉这段前缀
- state 取值:GitLab opened/closed/all(MR 另有 merged),其余平台 open/closed/all
- file 恒为读取:project、path、ref?(默认分支;超过字节上限截断并标记)
- bitbucket_repos 的 create 需要站点 defaultProject(workspace/repo)才能确定 workspace
- gitlab_projects 的 create 可传 namespace(组的完整路径,如 pandax 或 team/sub,或组的数字 id):客户端先把它解析成 namespace_id 再 POST /projects;不传则建在 token 所有者名下。这是唯一能指定归属的 create——其他平台的 create 始终落在 token 所有者名下(Bitbucket 为 defaultProject 的 workspace)
- Bitbucket 没有 releases API,因此没有 bitbucket_releases 工具;release 的 delete 用 release number(GitLab 按 tag 删除)
- 一个站点恰好拥有一个 token,以站点 id 为键存储:删除站点会连同 token 一起删除,token 也不会在站点之间共享。旧版本写下的存储(站点通过引用名共享 token)会在读取时自动迁移——每个站点引用到的值成为它自己的 token,没有任何站点引用的引用名会被丢弃
- GitLab 用 PRIVATE-TOKEN 头;GitHub、Gitee、Bitbucket 用 Authorization: Bearer(Gitee 在头被拒绝时自动兜底 access_token URL 参数);Gitea 用 Authorization: token
- HTTP 走 Node 内置 fetch 直连——刻意不用 ctx.web.fetch(只收 URL、无 header)

工作原理

~/.dsh/git-credentials.json(AES-256-GCM 加密:站点 + token 值) → 工具执行时解密一份快照,按 provider 过滤站点,读取该站点自己的 token → fetch(baseUrl/<provider api path>, { headers: { PRIVATE-TOKEN | Bearer | token } }) → 工具参数/返回值/错误信息里只有 site、project、path 等业务数据

开发

前置条件:一份 deepseek-harness 检出。开发工具链由 harness 提供:把 DSH_REPO 指向检出目录,并将其 node_modules/.bin 加入 PATH(@deepseek-ai/ 为私有包,通过 harness 的 tsconfig paths 解析)。

浏览器半边面向当前的客户端 slot 标准:面板是 settings.section 列表项,组件接收组合后的 section props;typecheck 程序通过 type-only import 引入 slot 契约。请针对实际运行的检出做类型检查——切换 harness 版本后重新生成 tsconfig.json。尚未构建客户端 face 的检出会回退到这些包的源码;如果报错指向 packages/.../src,先在检出里跑 pnpm run build:lib:client 构建该 face,再重新检查。

面板自带控件实现与文案。 src/client/panel-css.ts 把面板旁边那些宿主页面的控件尺寸、focus 行为与列表节奏抄进插件(类名统一加 dshgc- 前缀),与宿主共享的只有 --dsw- 主题 token,因此浅色/深色自动跟随。刻意不以模块方式 import @deepseek-ai/dsh-client-ui-primitives 等 Harness Client 包:它们会无预警地变动,而组件抛错会让整个 slot 条目变空白。dsh.client.inject 只用于排序激活,仍然允许。所有可见文案都通过客户端 locale 服务注册(src/client/locales.ts,中文 + 英文),组件里用注册项注入的 t 读取,因此面板正文与导航标题都跟随 Harness 的界面语言。

harness 兼容性由 manifest 强制把关。 DSH 会读取本包的 peerDependencies 中所有 @deepseek-ai/dsh 与 @deepseek-ai/dsh- 声明,只要有一个 range 不匹配当前运行的 harness 版本(含预发布版),就拒绝应用该 bundle 层:被拒的 bundle 不贡献任何工具与设置页,harness 会报告被拒的 peer。@deepseek-ai/dsh-tools 的 range 有意放宽为 >=0.1.7-rc.1 <1.0.0,使 0.x 线内的 harness 升级本身不会导致 bundle 被拒(harness 各包与产品版本同步发布)。因此遇到新 harness 版本时的流程是:重新生成 tsconfig.json、跑 pnpm typecheck 与 pnpm smoke;只有当这些真的失败时才动 range,并把 <1.0.0 当作重新验证的边界。不安装也能校验——这个检查只读 manifest:

node --input-type=module -e "
import { readFileSync } from 'node:fs'
const { evaluatePluginCompatibility, pluginCompatibilityWarning } = await import('$DSH_REPO/packages/boot/app-boot/lib/index.js')
const manifest = JSON.parse(readFileSync('./package.json', 'utf8'))
const issue = evaluatePluginCompatibility(manifest)
console.log(issue === undefined ? 'compatible' : pluginCompatibilityWarning(issue))
"

export DSH_REPO=/path/to/deepseek-harness
export PATH="$DSH_REPO/node_modules/.bin:$PATH"

为当前检出重新生成 tsconfig.json paths(已被 gitignore——机器相关)
pnpm gen:tsconfig

类型检查(含 browser half)
pnpm typecheck

keyless 冒烟:加密存储回读 + boot 断言 + 未配置 token 响亮失败(无网络、无模型 key)
DSH_REPO="$DSH_REPO" TSX_TSCONFIG_PATH="$DSH_REPO/tsconfig.json" \
node --import "$DSH_REPO/node_modules/tsx/dist/esm/index.mjs" smoke.ts

改过 src/client/ 后重建浏览器 bundle(运行中的 GUI 自动热替换)
pnpm build

- 组合/配置层:HMR 自动重组合,改完立即生效
- client bundle:webserver 轮询 + client-hmr 广播,重建后浏览器自动热替换
- host 插件源码:没有热更通道(Node 持有模块缓存,产品自身也没有 host 侧 watch);且包入口已是构建产物 lib/index.js,host 侧改动需先 pnpm build 再重启——或用「目录改名」技巧让模块 URL 全变,零重启热生效

$DSH_REPO/node_modules/.bin/tsdown 是 shell 包装脚本——直接执行(pnpm build 即如此),不要用 node .../.bin/tsdown。

目录结构

git-credentials/
package.json # dsh-git-credentials;peer: @deepseek-ai/{cordis,dsh-tools,dsh-schemastery} dsh.client 清单 + exports["./client"](browser half) cordis.patch.yml # bundle 补丁层(dsh.bundle.patch)——同时也是开发用 --patch 覆盖层 tsdown.config.ts # 自包含构建(node 半 + 浏览器 bundle,不依赖 harness 检出) smoke.ts # keyless 启动冒烟(含加密存储回读断言) tools/gen-tsconfig.mjs # 重新生成 tsconfig.json paths(DSH_REPO 驱动) src/index.ts # 插件入口:24 个工具注册 + 管理路由接线 src/store.ts # AES-256-GCM 加密存储(独立密钥、原子写、0600) src/http.ts # 共享 HTTP 助手(token 解析、分页、错误明细) src/gitlab.ts # GitLabClient(PRIVATE-TOKEN 头) src/github.ts # GitHubClient(Bearer 头 + User-Agent) src/gitee.ts # GiteeClient(Bearer 头,access_token URL 兜底) src/gitea.ts # GiteaClient(token 头) src/bitbucket.ts # BitbucketClient(Bearer 头,2.0 API) src/admin.ts # /git-credentials-admin/* 管理端点 src/client/ # browser half:设置页 Git 凭据分区 lib/ # 构建产物(node 半 + client bundle,已 gitignore) CHANGELOG.md # 逐版本变更;每次 release body 的来源

发布

包已按 dsh bundle 形态组织:dsh.bundle.patch 指向 cordis.patch.yml,用户执行 dsh plugin --profile <name> add dsh-git-credentials 即可安装并加入 profile 的 bundle 层。运行时通过安装自身的 flat fallback($DSH_HOME/profiles/node_modules)解析插件的 @deepseek-ai/ 依赖,因此 peerDependencies 声明的是 npm 已发布版本线(@deepseek-ai/cordis ^4.0.1-rc.1、@deepseek-ai/dsh-tools >=0.1.7-rc.1 <1.0.0、@deepseek-ai/schemastery ^3.18.1-rc.1)——切勿用开发工作区的 0.1.0-rc.5 版本。harness 各包与产品版本同步发布,且 DSH 只在这些 range 与当前 harness 匹配时才准入该 bundle;dsh-tools 的 range 有意覆盖整条 0.x 线,因此单纯的 harness 升级不会迫使插件发版(见「开发」)。

两个渠道分发同一份打包产物:

- npm —— pnpm publish 把构建好的包发布到公共 registry;publishConfig 已固定 https://registry.npmjs.org/,本地 npm 客户端即使配了镜像也不会发错地方。lib/ 被 gitignore 但在 files 白名单里,所以必须先构建:

pnpm build
pnpm publish

用户随后用 dsh plugin --profile <name> add dsh-git-credentials 安装。
- GitHub release —— 推送 v tag 会触发 GitHub Actions 在云端构建 node 半 + 浏览器 bundle、打包 tarball 并挂到 release。同一流程手动执行:

pnpm build
pnpm pack # -> dsh-git-credentials-<version>.tgz

把该 tarball 挂到 release,或本地直接安装:

dsh plugin --profile <name> add ./dsh-git-credentials-<version>.tgz

Release notes 来自 CHANGELOG.md。 推送 v* tag 时,CI 会取该文件里对应版本段落的正文作为 release body,并追加统一的安装 footer(.github/scripts/compose-release-notes.sh);某版本没有段落时回退到 GitHub 自动生成的 notes,因此不会因为漏写 changelog 卡住发布。打 tag 之前,请在版本号提交里把 [Unreleased] 的内容移入 ## [<version>] - <日期> 段落,这样 tag 指向的树里就带着这份 notes。想本地预览某个版本的正文:bash .github/scripts/compose-release-notes.sh <version>。

要重算已经发布的 release 的 notes(例如补齐早于这套流程的版本),手动运行 release workflow 并填 version 输入即可:它从默认分支读取 CHANGELOG.md、只重写 release body,不动已附带的 tarball 资产。

对外公布前先在本地验证产物:dsh plugin --profile <name> add <tarball|包名>,确认 dsh --profile <name> --dump-config 出现 # == dsh-git-credentials 层,再 boot profile 检查 24 个工具是否注册。

License

MIT
预览分享卡片长什么样 ↓revive/dsh-git-credentials
分享 · DeepSeek Harness Hub
revive/dsh-git-credentials

DeepSeek Harness 的独立外挂插件:管理 GitLab、GitHub、Gitee、Gitea 与…

这是你的仓库?认领条目

回到顶部