deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:jsoncode/dsh-jenkins
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
dsh-jenkins is a Jenkins management plugin built on the DeepSeek Harness (DSH) host, centralizing multiple servers and jobs for fast build publishing.
Available from the Settings page, a per-workspace entry, and model tools.
Screenshots of the Settings page, workspace entry and run/history modals: see preview.md.
settings.section): add / edit / delete
multiple servers (URL, username, Token), test connections, skip TLS verification.
Only Server URL and Token are required (username defaults to admin).$DSH_HOME/dsh-jenkins-map.json): every project's publish
targets in one file — project name → target array, with elements identical to a
workspace config file ({ name?, job, server, environments }). Each env can carry a
name display label (e.g. uat环境 / prod灰度 / prod环境) and the number of
envs is unlimited (an empty name falls back to UAT / Prod / Env N). A
dsh-jenkins.json/js/ts in a project root is discovered automatically (folder
name as the project name, missing-only — nothing you edited is overwritten), so
there is usually nothing to maintain by hand; to edit, click Edit map on the
Project Config row of the Config tab (form / JSON). See
Project config.sidebar.footer.action): a footer group with the Jenkins
logo button (opens the unified modal) and a History button (clock icon,
publish history of the last 50 runs across all workspaces, filterable by
workspace — defaults to All).
The Publish tab has just three rows — project → server → job — then the
parameter form, build triggering and status polling (queued → building → result,
10-minute timeout). The env has no row of its own: each env in a project
config is a server, so env selection lives in the Server dropdown — whose
labels show only the plugin's server name (never a mix of the config's env
name and the server name), and picking one switches the job / params to that env.
Config server refs are matched against configured servers by
name → id → full URL → domain (the domain level ignores scheme, port and
context path). The dropdown shows the intersection of the servers referenced by
the project config and the servers configured in the plugin; the last
parameters are remembered per project and auto-echoed next time.dsh_jenkins_build, dsh_jenkins_status.Config + a plugin data file
$DSH_HOME/dsh-jenkins.json (server tokens encrypted with the machine-bound key
$DSH_HOME/dsh-jenkins.key, cache in plaintext); the project config is its own
file $DSH_HOME/dsh-jenkins-map.json (plaintext, hand-editable). On first run any
legacy dsh-jenkins namespace in settings.yaml is migrated once and cleared; a
legacy projects field inside dsh-jenkins.json is migrated into the new file
(missing-only).dsh.bundle + dsh.client(web) manifests.├── src/host/*.ts # Host half: index.ts (entry), jenkins.ts (curl core), ops.ts (op dispatch), project-map.ts (project config file), projects.ts (normalize/merge), workspace-config.ts, types.ts
├── src/client/*.tsx # Browser half (React TSX): settings page, footer entry, publish modal, project config modal, history modal
├── lib/index.js # Host half build artifact (tsdown, ESM), committed for git installs
├── lib/client.js # Browser half build artifact (tsdown → __ModuleLoader__ factory), committed
├── lib/types/ # Type declarations (generated by tsc -b)
├── scripts/ # verify-client.mjs (host-seed simulation check) + isolated tests
├── examples/ # sample configs: dsh-jenkins.json (workspace array), dsh-jenkins-map.json (central map)
├── tsdown.config.ts # tsdown build config (node half + client bundle banner wrapper)
├── tsconfig.json # solution: references tsconfig.host.json / tsconfig.client.json
├── cordis.patch.yml # Bundle patch: plugin row referenced by package name (no paths)
├── package.json # dsh.bundle + dsh.client(web) manifests + peerDependencies
├── README.md # This file (English)
├── README.zh.md # 中文文档
└── preview.md # Screenshot preview (references assets/preview/*.png)
dsh-jenkins.json / .js / .ts)Place it in the workspace root. It is an array; each element is one deploy
target (job + server + environments params). .json is parsed directly; .js / .ts
are evaluated with node (CJS module.exports or ESM export default):
[
{
"job": "build-app",
"server": "http://uat.example.com",
"environments": { "BRANCH": "main", "DEPLOY": false }
},
{
"job": "build-app",
"server": "http://prod.example.com",
"environments": { "BRANCH": "release-1.0", "DEPLOY": true }
}
]
job (Jenkins job path, e.g. build-app or
folder/build-app) and server (the server name / id / URL as configured in
Settings → Jenkins).environments (optional): the parameter map for this target (booleans render as
checkboxes, everything else as text fields).dsh-jenkins-map.json)One config for every project: project name → target array. Each env may carry a
name display label (e.g. uat环境 / prod灰度 / prod环境) and the number of
envs is unlimited (env 1 is the default, usually UAT); when the name is empty the
UI falls back to UAT / Prod / Env N. Elements are identical to a workspace config
file, so they can be moved back and forth:
{
"health-check-ui": [
{
"name": "uat环境",
"job": "system3_Front_docker3",
"server": "https://dev-jenkins-tx.whale-plus.com",
"environments": {
"project": "health-check-ui",
"branch": "uat5",
"NodeVersion": "v24.12.0",
"INSTALL_COMMAND_ACTIVE": "pnpm i --registry=https://repo.huaweicloud.com/repository/npm/",
"BUILD_COMMAND_ACTIVE": "pnpm build:uat"
}
},
{
"name": "prod灰度",
"job": "pro_system3_Front_docker3_gray",
"server": "https://jenkins-tx.whale-plus.com",
"environments": {
"project": "health-check-ui",
"branch": "release/gray",
"NodeVersion": "v24.12.0",
"BUILD_COMMAND_ACTIVE": "pnpm build:gray"
}
},
{
"name": "prod环境",
"job": "pro_system3_Front_docker3",
"server": "https://jenkins-tx.whale-plus.com",
"environments": {
"project": "health-check-ui",
"branch": "master5",
"NodeVersion": "v24.12.0",
"BUILD_COMMAND_ACTIVE": "pnpm build:prod"
}
}
]
}
$DSH_HOME/dsh-jenkins-map.json (a bare map,
no wrapper, plaintext; a missing file loads as {}, a corrupt one is backed up to
.bak and treated as empty). A legacy projects field inside dsh-jenkins.json
is migrated into this file on first startup (missing-only).name (env display label): optional; an empty string is dropped (no
"name": "" is written). It shows up in the Server dropdown label and in Local
Records, so a build's env is obvious at a glance.dsh-jenkins.json/js/ts and merges it under the folder name —
missing-only by default, so hand-edited projects are never clobbered. To pull
in updated workspace configs, tick Overwrite same-name projects and hit
Rediscover in the modal footer.dsh-jenkins-map.json · N projects) → Edit map:server refs may be a server name / id / full URL / bare domain; they are
matched against configured servers by name → id → full URL (trailing slash
ignored) → domain, where the domain level ignores scheme, port and context path
(http://jenkins-tx.example.com:8080/jenkins equals the configured
https://jenkins-tx.example.com). When nothing matches, the Publish tab shows a
hint under the server row and the dropdown falls back to all servers.# Local development
dsh plugin --profile web add ./dsh-jenkins
# Published: npm / tarball / GitHub
dsh plugin --profile web add dsh-jenkins
dsh plugin --profile web add ./dsh-jenkins-0.1.4.tgz
dsh plugin --profile web add github:you/dsh-jenkins#<sha>
dsh --profile web --dump-config # verify the layer
dsh --profile web # start (restart required for the host half to reload)
Local development dependencies: the host loads
index.jsthrough native Node ESM, so@deepseek-ai/schemastery,@deepseek-ai/dsh-toolsand@deepseek-ai/dsh-settingsmust be resolvable from the plugin directory (node_modulesis gitignored). Either:
- run
pnpm installinside the plugin directory (these three are declared as devDependencies); or- junction the host's flat fallback copies, e.g.:
New-Item -ItemType Directory "$PWD\node_modules\@deepseek-ai" -Force foreach ($p in 'schemastery','dsh-tools','dsh-settings') { New-Item -ItemType Junction "$PWD\node_modules\@deepseek-ai\$p" -Target "$env:DSH_HOME\profiles\node_modules\@deepseek-ai\$p" }
Static server defaults can also be set in the profile's cordis.patch.yml:
- insert:
- id: dsh-jenkins
name: dsh-jenkins
config:
servers:
- id: prod
name: 生产环境
baseUrl: https://jenkins.example.com
username: admin
token: <API Token or password>
insecure: false
The build toolchain is tsc + tsdown (same as @lemcae/dsh-balance and other
similar plugins — no vite): tsc -b type-checks and emits declarations, while
tsdown (Rolldown core) bundles the host half (lib/index.js, ESM) and the
browser half (lib/client.js, single-file CJS __ModuleLoader__ factory with
auto banner wrapping). Dependency management uses pnpm 10 (Node 26; the
pnpm-lock.yaml is committed and CI installs with --frozen-lockfile):
pnpm install # install per pnpm-lock.yaml
pnpm run build # clean lib → tsc -b (types + declarations) → tsdown (both halves)
pnpm run verify # simulate the host module table to check lib/client.js (optional)
pnpm publish # or pnpm pack / git push origin main (lib/ is committed; git installs need no build)
Pushing a v* tag (pnpm run release bumps the patch version, rebuilds the
artifact, and tags it automatically) triggers
.github/workflows/publish.yml:
pnpm install --frozen-lockfile →
pnpm run check (tsc -b) → pnpm run build (tsc -b && tsdown) →
pnpm pack → creates a GitHub Release (auto-generated changelog, tarball
attached);NPM_TOKEN repository
secret (Settings → Secrets and variables → Actions); fails fast with a hint
when it is missing.Requirements: Node ≥ 26 + pnpm 10 (the packageManager field in
package.json pins the pnpm version).
pnpm install # devDependencies: typescript, tsdown, @types/react, @deepseek-ai/* type packages, etc.
pnpm run check # whole-tree TypeScript type check (tsc -b)
pnpm run build # rebuild both halves after editing source (tsc -b && tsdown)
pnpm run watch # tsdown watch mode (rebuild on src/client changes)
pnpm run verify # simulate the host seed table to check lib/client.js loads
pnpm run test # isolated tests: curl dump parsing + failure log + param parsing + centralized project config
pnpm run test:params # param parsing: built-in types / uno-choice / Extended Choice / build-page fallback
pnpm run test:store # data-file round-trip: token encryption / migration / keep-token semantics
src/host/; browser half in src/client/ (build entry
src/client/index.ts, exporting { name, inject, apply } directly);window.__ModuleLoader__.load factory wrapper of lib/client.js is
generated by tsdown's banner/intro/footer options (no hand-written wrap
script);react,
@deepseek-ai/dsh-client-ui-primitives, ...) stay external and resolve from
the host module table (seed) at runtime.The Publish tab renders its parameter form from the server-side definitions:
| Server-side type | Control |
|---|---|
StringParameterDefinition / uno-choice dynamic reference |
single-line text |
TextParameterDefinition |
textarea |
BooleanParameterDefinition |
checkbox |
PasswordParameterDefinition / CredentialsParameterDefinition / FileParameterDefinition |
password / text field |
ChoiceParameterDefinition, uno-choice ChoiceParameter / CascadeChoiceParameter, Extended Choice single |
searchable dropdown |
Extended Choice multi-select / uno-choice MultiSelectParameter |
checkbox list (submitted joined by the delimiter) |
ChoiceParameter,
CascadeChoiceParameter) expose only _class + default in REST /api/json — the
option list is computed by Groovy at render time. Those params automatically
fall back to the build page HTML (job/<path>/build) and are parsed from its
<select> options, so a project dropdown lists every project. When it still
cannot be resolved the field degrades to a text input with an inline hint instead
of showing an empty dropdown.defaultValue (built-ins) or
defaultParameterValue.value (plugin types such as uno-choice), so
project=boss_backend is pre-filled correctly.DynamicReferenceParameter entries (empty name)
never become empty fields — dash-only ones are dropped, ones with text render as a
dashed divider note.Every failed request (job list / job detail / build history / trigger / status / build log /
connection test …) is appended to $DSH_HOME/dsh-jenkins.log (same directory as
dsh-jenkins.json; on Windows C:\Users\<you>\.dsh\dsh-jenkins.log) as JSONL — one line per
failure:
{"time":"2026-09-14T07:02:19.949Z","level":"error","op":"jobs","code":"http-401",
"message":"auth failed (HTTP 401)","server":"UAT <https://jenkins.example.com>","user":"jason",
"request":"GET /api/json?tree=jobs[...]","httpStatus":401,"httpStatuses":[200,401],
"curlExit":0,"curlStderr":"","bodySnippet":"<html>...Error 401 Unauthorized...</html>"}
httpStatuses like [200, 401] means curl went through an HTTP proxy: the proxy's CONNECT
tunnel block (200 Connection Established) is printed first, then the real response block —
the last entry is the real status;dsh-jenkins.log.1 above 2MB (one generation kept); logging never breaks
the main flow.| Symptom (log field / UI text) | Cause |
|---|---|
code=parse-failed with a bodySnippet starting with HTTP/1.1 |
HTTPS through an HTTP proxy (https_proxy): curl's -D - prints the proxy's 200 Connection Established tunnel block first; the old code split at the first blank line and swallowed the real headers into the body. Fixed by block-aware parsing (parseCurlDump) |
code=auth-failed (HTTP 401) |
Wrong or expired username/Token (re-run Test connection in settings) |
code=forbidden (HTTP 403) |
Token lacks permission / CSRF missing / reverse proxy blocking |
code=network-failed with curlExit=7/28/35/60 |
DNS, connection refused (7), timeout (28, 40s cap), TLS handshake (35), self-signed cert (60 — enable "ignore certificate") |
code=redirect |
URL is not the final one (http:// → https://, missing context path); redirects are not followed, the log carries Location |
code=response-too-large |
Response exceeded the host's 8MB collection cap (tail kept); narrow the request |
code=empty-response |
No response header block: proxy ate the response, connection cut, or output truncated |
code=server-missing |
The client's cached server id no longer exists in the config (pick a server again) |
code=curl-unavailable |
Host subprocess service unavailable / curl cannot start |
stage=route-guard |
Request was rejected by the /dsh-jenkins/api trust fence (non-loopback Host, cross-site marker) — it never reached plugin logic |
| Empty job list but no failure | Folders deeper than the 3-level tree are returned as folder placeholders and filtered out by the UI; the instance nests jobs too deeply |
curl.exe spawned directly through the host subprocess service: Basic auth--data-binary @- (form body over stdin, UTF-8 without BOM); the -D - output is
parsed per response block (parseCurlDump: skip the proxy CONNECT / 1xx blocks, take the
last real block for status and Location), so a tunnel block's 200 no longer masks the real status.$DSH_HOME/dsh-jenkins.log (see above): jenkins.ts records the HTTP /
curl evidence, index.ts records op-level failures at all three entry points (route, command,
model tool)./dsh-jenkins/api route registered on webServer
(fetch POST JSON → { ok, value } envelope, behind a trust fence); older hosts fall back to the
command channel ctx.remote.commands.execute(sessionId, '/dsh-jenkins <json>'). Host errors carry
a code that the client localizes (fallback to the raw message).@deepseek-ai/cordis, dsh-tools, schemastery, dsh-settings,
dsh-commands, dsh-session, dsh-api-remotes, client runtime/ui-slots/ui-settings/
cordis-client-runner, react) are resolved by the host at install time.deepseek-harness project is not modified; all features use existing
slots (sidebar.footer.action, settings.section, shell.overlay) and the command
transport..dshj-*
with one deliberate exception — :where(div:has(> [data-slot="sidebar.footer.action"] > .dshj-footer-group)){flex-direction:column},
which stacks the host footer container (the host lays it out as a flex row, so
several plugin entries would squeeze onto one line). It can only match a container
that already holds this plugin's own entry, and :where() drops its specificity
to 0 so the host can always override it. Keyframe names are dshj--prefixed and the
style tag is marked data-plugin-css="dsh-jenkins/settings.css"; no other global
selector, no :root/body/* rule, no body-style mutation.rgba(0,0,0,.32) scrim
with blur(12px) saturate(1.2), a color-mix(bg-layer-1 78%) glass panel with a
border-l2 hairline and 14px radius, border-l1 header/footer dividers, solid
button-primary-fill primary buttons and active tabs (translucent fills washed the
host's monochrome #0f1115 / #f9fafb primary into grey), bg-base inputs and
dropdown panels, bg-layer-2 cards, and state-* tokens for status colours.CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。