
Security News
Deno Goes the Way of the Dinosaur
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.
dsh-jenkins
Advanced tools
Jenkins CLI plugin (dual-face): multi-server config page, model tools, and a per-workspace Run-Jenkins-Job entry with environment tabs. Bilingual UI (zh/en), no hardcoded paths, publishable to npm / GitHub
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.FAQs
Jenkins CLI plugin (dual-face): multi-server config page, model tools, and a per-workspace Run-Jenkins-Job entry with environment tabs. Bilingual UI (zh/en), no hardcoded paths, publishable to npm / GitHub
The npm package dsh-jenkins receives a total of 0 weekly downloads. As such, dsh-jenkins popularity was classified as not popular.
We found that dsh-jenkins demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.

Security News
Anthropic’s free OSS Scanner uses Mythos and other Claude models to find open source vulnerabilities and send maintainers reports with proposed fixes.

Security News
On Front Lines’ Early Adopters: CISO podcast, Andrew Becherer shares lessons from Datadog, weighs the long-term costs of AI-built tools, and explains how founders can actually gain a CISO’s trust.