DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
G

dsh-git-credentials

Git Credentials

树外 dsh 插件:GitLab、GitHub、Gitee、Gitea 和 Bitbucket 令牌不会进入模型上下文,而是以加密形式(AES-256-GCM)存储在插件专属文件中;模型按需调用代码托管平台 API 工具,Web 设置页面用于管理站点和令牌。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:revive/dsh-git-credentials#ec142771aa57139c0310f55a39d43ba1204f93e7
README兼容性版本

兼容性与来源证明

Git Credentials 以 dsh-git-credentials 发布,当前版本为 0.7.0。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/11/4

版本

0.7.0stable
2026/11/3
0.6.2stable
2026/11/3
0.6.1stable
2026/10/22
查看其余 10 个版本收起版本
0.6.0stable
2026/10/22
0.5.0stable
2026/10/9
0.4.0stable
2026/10/5
0.3.4stable
2026/9/29
0.3.3stable
2026/9/24
0.3.2stable
2026/9/9
0.3.1stable
2026/9/3
0.3.0stable
2026/9/3
0.2.2stable
2026/8/27
0.1.1stable
2026/8/20

相关插件

正在加载相关插件…

最新版
0.7.0
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 3
周下载
394
最近提交
2026/11/3
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 security-access 分类下经过校验的插件。

Cc Safety Netcc-safety-net编码代理 CLI 钩子——阻止破坏性命令和访问机密文件Pocketdsh-pocket把 DeepSeek Harness 装进你的口袋:一个包、一个设置页,手机扫码即同步访问电脑上的 DSH(局域网 + 公网,实时同屏)。Codex Subscriptiondsh-codex-subscription在 DeepSeek Harness 中通过 OAuth 使用 ChatGPT 和 Codex 订阅,支持配额、安全重置、网络搜索、图像和快速模式,并可通过 ChatGPT 移动应用(Beta)控制 DSHXdpooldsh-xdpool将你拥有的所有免费额度汇聚为一个 DSH 模型池:合并所有本地登录的 WorkBuddy 账户并自动故障转移,通过二维码/网页登录 13 个供应商,汇总 76 个供应商的免费层额度,此外还提供用量仪表盘、模型路由和本地 OpenAI 兼容网关

README

dsh-git-credentials

简体中文

An out-of-tree plugin for DeepSeek Harness that manages GitLab, GitHub, Gitee, Gitea, and Bitbucket API tokens so token values never enter the model context.

The model's tools carry only a site id (e.g. corp); that site's token is decrypted from the plugin's own encrypted store at call time and appears only in the outgoing HTTP Authorization header. Changing a site or rotating its token takes effect on the very next call — no restart required.

Features

  • Tokens stay out of the model context — never in tool arguments, return values, or error messages; only business data (site, project, path, …) crosses the model boundary
  • Encrypted at rest — AES-256-GCM encrypted data file with a separate 32-byte random key file (0600, atomic writes)
  • Per-provider tool scoping — gitlab_* tools only see GitLab sites, github_* only GitHub sites, and likewise for gitee_*, gitea_*, bitbucket_*; unconfigured sites/tokens fail loud with the valid values listed in the error
  • Web settings panel — add, edit, and delete sites; store or clear token values; no response ever carries a token value, only configured state
  • Hot load/unload — mounts and unmounts on a running GUI without restarting it
  • Instant effect — each tool call reads a freshly decrypted snapshot, so edits and rotations apply immediately

Why not just an MCP server?

GitHub publishes an official MCP server, and the harness supports MCP clients natively — for GitHub-only automation, wiring up the official MCP server is the mainstream choice, and this plugin's github_* tools do overlap with it.

This plugin earns its place where MCP servers don't cover the gap:

Official GitHub MCPThis plugin
ForgesGitHub only (GitLab has an official server; Gitee / Gitea / Bitbucket rely on third-party servers of varying quality and maintenance)One encrypted store, one settings panel, one tool set for GitLab, GitHub, Gitee, Gitea, and Bitbucket — including self-hosted Gitea / GitLab
Token handlingPlaintext environment variables per server, no management UIAES-256-GCM encrypted storage, one token per site, settings-page management; token values never enter the model context
IntegrationExtra MCP proxy processTools register directly in the harness tool registry

Use the MCP route for a single hosted forge with standard token handling; use this plugin for multi-forge setups (especially Gitee or self-hosted Gitea), or when you want encrypted storage plus an in-product management page.

Security model

Storage

  • ~/.dsh/git-credentials.json — data file, fully encrypted with AES-256-GCM (0600, atomic write)
  • ~/.dsh/git-credentials.key — 32-byte random key, stored as a separate file (0600)

Threat model

ScenarioProtected?
A human copies/backs up/syncs the data file✅ Yes — ciphertext only; without the key file it cannot be decrypted
A same-UID process (e.g. the agent's bash/fs tools) reads both files❌ No — the key sits beside the data with the same permissions; same level as the harness's own key handling ("discretion, not a boundary")
The user deliberately asks the model to read the files❌ No — out of scope; no system can stop that

Losing the key file means the data is unrecoverable (decryption fails loud and reports the key path); a copied data file alone is safe.

Installation

Option A: install the release tarball (recommended)

Download dsh-git-credentials-<version>.tgz from the releases page — the tarball ships the built browser bundle, so no harness checkout or build step is needed — then install it into a profile with the dsh CLI:

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

The first use initializes the profile, pnpm links the package, and dsh appends the plugin to the profile's bundle layers. Verify the layer without booting:

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

Installing a bundle does not hot-mount into a running GUI: bundle layers are composed at boot (HMR hot-applies only patch files), so restart the GUI process after dsh plugin add. After the restart, the plugin appears under Settings → Git Credentials.

Option B: install from a source checkout

The plugin is a pure add-on — zero changes to harness code. Two entries under ~/.dsh are enough:

  1. Symlink the plugin directory so every profile can resolve the package:

    mkdir -p ~/.dsh/profiles/node_modules
    ln -s /path/to/dsh-git-credentials ~/.dsh/profiles/node_modules/dsh-git-credentials
    
  2. Add the plugin row to the home-layer overlay ~/.dsh/cordis.patch.yml (applies to every profile, web and headless alike):

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

The HMR watcher monitors the home layer: adding the row hot-mounts the plugin into a running GUI, removing it (or disabled: true) hot-unmounts it, and config edits hot-reconfigure it. Uninstalling = removing both entries.

The browser half (lib/client.js) is a build artifact — after cloning, build it first (see Development). The release tarball already contains it.

Usage

Manage sites and tokens in Settings → Git Credentials:

  • Add a site: the form sits in its own Add site card below the configured list, with site id, provider (GitLab / GitHub / Gitee / Gitea / Bitbucket), API base URL (defaults per provider: https://api.github.com, https://gitee.com/api/v5, https://api.bitbucket.org/2.0; GitLab and Gitea are self-hosted and need their own address, e.g. https://gitlab.example.com / https://gitea.example.com/api/v1), the site's token, and an optional default project. One Save writes the site and its token together; the field rules (site id charset, http(s) base URL) appear as field help, and an invalid draft says what is wrong and disables Save instead of failing server-side
  • Each configured site: read-only by default (provider, base URL, default project, token state) in its own card, with Edit and Delete site; edit mode reveals the inputs plus Save / Cancel, saves the fields and a newly typed token in one action, and offers Clear token beside the token field. Deleting a site deletes its token with it
  • One site, one token: a token belongs to the site it was saved with, so it can neither outlive its site nor be shared by accident
  • Token help: the token field links to the selected provider's token-creation page — GitHub's fine-grained PAT page (name prefilled), Gitee's and Bitbucket's fixed pages, and GitLab's or Gitea's page derived from the site's API base URL (the link is hidden when that URL is empty or unparseable). While no token is configured, the field also names the platform's minimum scopes/permissions. The link is built locally and carries no token
  • The panel follows the Harness UI language: its copy and its navigation label are registered in Chinese and English through the client locale service
  • The panel talks to same-origin /git-credentials-admin/* JSON endpoints; token values never appear in any response
  • All changes take effect immediately — every tool call reads a fresh decrypted snapshot

Tools

One resource tool per provider, with an action parameter selecting the operation. List actions return the provider's canonical summary shapes (repos: {id, path, name, webUrl, visibility}; issues/PRs: {number|iid, title, state, webUrl, authorName}; file: {path, ref, content, truncated}). get reads one issue or merge/pull request in full — that summary plus body, labels, createdAt, updatedAt, bodyTruncated, and (on merge/pull requests) sourceBranch, targetBranch, and draft; the body is capped at the configured byte limit and a capped read is flagged. comments lists the comments of one issue/PR/MR and comment creates one; both return {id, body, authorName, createdAt, updatedAt, webUrl, bodyTruncated}, with the body capped the same way. Write actions perform a real mutation — the model should confirm with the user before calling them.

ToolactionParameters
gitlab_projectslist, createlist: search?, membership?, perPage? · create: name, namespace?, description?, path?, visibility?
gitlab_filereadproject, path, ref?
gitlab_merge_requestslist, get, create, merge, close, comment, commentsproject?, state?, perPage?, number, title, sourceBranch, targetBranch, body?
gitlab_issueslist, get, create, close, reopen, comment, commentsproject?, state?, perPage?, number, title, body?
github_reposlist, createlist: search?, perPage? · create: name, description?, private?
github_filereadproject (owner/repo), path, ref?
github_pull_requestslist, get, create, merge, close, comment, commentsproject?, state?, perPage?, number, title, head, base, body?
  • action defaults to the read operation (list, or read for file) — existing read callers keep working unchanged
  • number is the issue/PR number (iid on GitLab); required for get / close / reopen / comment / comments / merge
  • Gitee issue ids are strings such as IKRZSO: gitee_issues accepts either an integer or that string for number. Gitee creates and updates issues through owner-level endpoints (the client sends repo in the body)
  • comments and comment work on issues and merge/pull requests: GitHub and Gitea read general PR comments from their issue-comments endpoint, GitLab reads notes and filters system notes, Gitee reads issue or pull-request comments (its sparse PR create response is re-read), and Bitbucket skips deleted comments
  • Every client keeps the path segment of the site's API base URL (/api/v5 on Gitee, /api/v1 on Gitea, /2.0 on Bitbucket, /api/v3 on GitHub Enterprise); an absolute endpoint path must not be resolved against that base
  • state values: GitLab opened/closed/all (merged for MRs), the others open/closed/all
  • file always reads: project, path, ref? (defaults to the repository default branch; content over the byte cap is truncated and flagged)
  • bitbucket_repos create needs the site's defaultProject (workspace/repo) to know which workspace to create in
  • gitlab_projects create takes namespace (a group's full path such as pandax or team/sub, or its numeric id); the client resolves it to namespace_id before POST /projects. Omit it to create under the token owner. This is the only create that can choose an owner — every other provider's create lands under the token owner (Bitbucket: the defaultProject workspace)
  • Bitbucket has no releases API, so no bitbucket_releases tool; release delete uses the release number (GitLab deletes by tag)
  • One site owns exactly one token, stored under the site id: deleting a site deletes its token, and a token cannot be shared between sites. A store written by an earlier version (where sites named a shared token reference) is migrated on read — each site's referenced value becomes its own token, and reference keys no site used are dropped

How it works

~/.dsh/git-credentials.json (AES-256-GCM encrypted: sites + token values)
  → tool execution decrypts one snapshot, filters sites by provider, reads the site's own token
  → fetch(baseUrl/<provider api path>, { headers: { PRIVATE-TOKEN | Bearer | token } })
  → tool arguments/returns/errors carry only business data (site, project, path, …)

Development

Prerequisites: a clone of deepseek-harness. The dev toolchain is harness-backed: point DSH_REPO at the checkout and put its node_modules/.bin on PATH (the harness's @deepseek-ai/* packages are private and resolve through its tsconfig paths).

The browser half targets the current client slot standard: the panel is a settings.section list entry whose component receives the composed section props, and the typecheck program pulls the slot contracts through type-only imports. Typecheck against the checkout you actually run — regenerate tsconfig.json after switching harness versions. A checkout that has not built its client face yet falls back to those packages' sources; if the report names errors inside packages/.../src, build the face first with pnpm run build:lib:client in the checkout and typecheck again.

The panel brings its own controls and its own copy. src/client/panel-css.ts copies the control metrics, focus behavior, and list rhythm of the host pages the panel sits beside, renamed under a dshgc- prefix, and the only thing shared with the host is the --dsw-* theme tokens — so light and dark follow automatically. Harness Client packages (such as @deepseek-ai/dsh-client-ui-primitives) are deliberately not imported as modules: they change without notice, and a throwing component blanks the slot entry. dsh.client.inject entries only order activation and remain allowed. All visible text is registered through the client locale service (src/client/locales.ts, Chinese and English) and reached through the framework-injected t of the section registration, so the panel and its navigation label follow the Harness UI language.

Harness compatibility is enforced from the manifest. DSH reads this package's peerDependencies on @deepseek-ai/dsh and @deepseek-ai/dsh-* and refuses to apply the bundle layer unless every range matches the running harness version (prereleases included); a refused bundle contributes no tools and no settings page, and the harness reports the refused peers. The @deepseek-ai/dsh-tools range is deliberately wide — >=0.1.7-rc.1 <1.0.0 — so that a harness bump inside the 0.x line never refuses the bundle on its own (harness packages version in lockstep with the product). A new harness release is therefore handled as: regenerate tsconfig.json, pnpm typecheck, pnpm smoke; touch the range only when one of those actually fails, and treat <1.0.0 as the re-validation boundary. Verify a package against a checkout without installing it — the check itself takes only the 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"

# Regenerate tsconfig.json paths for this checkout (gitignored — machine-specific)
pnpm gen:tsconfig

# Typecheck (including the browser half)
pnpm typecheck

# Keyless smoke: encrypted-store round-trip + boot assertions + loud failure
# for unconfigured tokens (no network, no model 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

# Rebuild the browser bundle after touching src/client/ (the live GUI hot-swaps it)
pnpm build
  • Composition/config layers recombine via HMR immediately — no restart
  • Client bundle is picked up by the webserver's stat-poll + client-hmr broadcast — rebuild and the browser hot-swaps it
  • Host plugin source has no hot path (Node caches modules; the harness has no host-side watch) — and since the package entry is the built lib/index.js, host-side edits need a pnpm build before the restart; or rename the plugin directory so module URLs change and hot-swap zero-restart

$DSH_REPO/node_modules/.bin/tsdown is a shell shim — run it directly (as pnpm build does), not via node .../.bin/tsdown.

Project structure

git-credentials/
  package.json            # dsh-git-credentials; peers: @deepseek-ai/{cordis,dsh-tools,dsh-schemastery}
                          # dsh.client manifest + exports["./client"] (browser half)
  cordis.patch.yml        # bundle patch layer (dsh.bundle.patch) — also the dev --patch overlay
  tsdown.config.ts        # self-contained build (node half + browser bundle, no harness checkout)
  smoke.ts                # keyless boot smoke (incl. encrypted-store round-trip assertions)
  tools/gen-tsconfig.mjs  # regenerates tsconfig.json paths for this checkout (DSH_REPO-driven)
  src/index.ts            # plugin entry: 24 tool registrations + admin route wiring
  src/store.ts            # AES-256-GCM encrypted storage (independent key, atomic write, 0600)
  src/http.ts             # shared HTTP helpers (token resolution, pagination, error detail)
  src/gitlab.ts           # GitLabClient (PRIVATE-TOKEN header)
  src/github.ts           # GitHubClient (Bearer header + User-Agent)
  src/gitee.ts            # GiteeClient (Bearer header, access_token URL fallback)
  src/gitea.ts            # GiteaClient (token header)
  src/bitbucket.ts        # BitbucketClient (Bearer header, 2.0 API)
  src/admin.ts            # /git-credentials-admin/* management endpoints
  src/client/             # browser half: the Settings → Git Credentials panel
  lib/                    # build output (node half + client bundle, gitignored)
  CHANGELOG.md            # per-version changes; the source of each release body

Publishing

The package is shaped as a dsh bundle: dsh.bundle.patch points at cordis.patch.yml, so users install it with dsh plugin --profile <name> add dsh-git-credentials and it joins the profile's bundle layers. The runtime resolves the plugin's @deepseek-ai/* imports from the installation's flat fallback ($DSH_HOME/profiles/node_modules), so the peerDependencies declare the published version line (@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) — never the dev-workspace 0.1.0-rc.5 versions. Harness packages version in lockstep with the product, and DSH admits a bundle only while those ranges match the running harness; the dsh-tools range deliberately spans the whole 0.x line, so a harness bump alone never forces a plugin release (see Development).

Two channels carry the same packed artifact:

  • npm — pnpm publish ships the built package to the public registry; publishConfig pins https://registry.npmjs.org/, so a mirror configured in the local npm client cannot misroute the publish. lib/ is gitignored but whitelisted in files, so build first:

    pnpm build
    pnpm publish
    

    Users then install with dsh plugin --profile <name> add dsh-git-credentials.

  • GitHub release — pushing a v* tag triggers GitHub Actions to build the node half + browser bundle in the cloud, pack the tarball, and attach it to the release. The same pipeline, by hand:

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

    Attach that tarball to the release, or install it directly:

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

Release notes come from CHANGELOG.md. Pushing a v* tag composes the release body from that file's section for the tag's version and appends the standard install footer (.github/scripts/compose-release-notes.sh); a version without a section falls back to GitHub's generated notes, so a release never blocks on a changelog omission. Before tagging, move the [Unreleased] items into a ## [<version>] - <date> section in the version-bump commit, so the tagged tree carries the notes. Preview one body locally with bash .github/scripts/compose-release-notes.sh <version>.

To (re)compose the notes of a release that is already published — for example to backfill one that predates this pipeline — run the release workflow manually with its version input: it reads CHANGELOG.md from the default branch, rewrites the release body, and leaves the attached tarball untouched.

Verify the artifact locally before announcing it — dsh plugin --profile <name> add <tarball|package>, confirm dsh --profile <name> --dump-config shows the # == dsh-git-credentials layer, then boot the profile and check the 24 tools register.

License

MIT

github_issueslist, get, create, close, reopen, comment, commentsproject?, state?, perPage?, number, title, body?
gitee_reposlist, createlist: search?, perPage? · create: name, description?, private?
gitee_filereadproject (owner/repo), path, ref?
gitee_pull_requestslist, get, create, merge, close, comment, commentsproject?, state?, perPage?, number, title, head, base, body?
gitee_issueslist, get, create, close, reopen, comment, commentsproject?, state?, perPage?, number, title, body?
gitea_reposlist, createlist: search?, perPage? · create: name, description?, private?
gitea_filereadproject (owner/repo), path, ref?
gitea_pull_requestslist, get, create, merge, close, comment, commentsproject?, state?, perPage?, number, title, head, base, body?
gitea_issueslist, get, create, close, reopen, comment, commentsproject?, state?, perPage?, number, title, body?
gitlab_releaseslist, create, deleteproject?, perPage?, tag (create requires; delete deletes by tag on GitLab), name?, body?, draft?, prerelease?
github_releaseslist, create, deleteproject?, perPage?, tag, number (release id, required for delete), name?, body?, draft?, prerelease?
gitee_releaseslist, create, deleteproject?, perPage?, tag, number (release id, required for delete), name?, body?, draft?, prerelease?
gitea_releaseslist, create, deleteproject?, perPage?, tag, number (release id, required for delete), name?, body?, draft?, prerelease?
bitbucket_reposlist, createlist: search?, perPage? · create: name, description?, private?
bitbucket_filereadproject (workspace/repo), path, ref?
bitbucket_pull_requestslist, get, create, merge, close, comment, commentsproject?, state?, perPage?, number, title, head, base, body?
bitbucket_issueslist, get, create, close, reopen, comment, commentsproject?, state?, perPage?, number, title, body?
  • GitLab authenticates with the PRIVATE-TOKEN header; GitHub, Gitee, and Bitbucket with Authorization: Bearer (Gitee additionally falls back to the access_token URL parameter when the header form is rejected); Gitea with Authorization: token
  • HTTP goes through Node's built-in fetch directly — ctx.web.fetch is deliberately not used (URL-only, no header support)