dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
dsh-webhook-dispatcher gives a dsh deployment inbound webhook dispatch: when an HTTP caller reaches one of the plugin's endpoints, a fresh, isolated agent session handles the delivered content.
dsh-webhook-dispatcher gives a dsh deployment inbound webhook dispatch: when an HTTP caller reaches one of the plugin's endpoints, a fresh, isolated agent session handles the delivered content. External systems such as CI pipelines, issue trackers, or monitoring tools can drive dsh sessions directly - no per-service bridge code.
429 + Retry-After backpressurewebhook status tool verb reports live routes, queue depth, and
recent delivery history; the plugin creates no config filessessionRetention window, keeping the framework
session store boundeddsh-webhook-dispatcher is an out-of-tree plugin for a dsh profile. Install it with the dsh plugin command:
dsh plugin --profile <name> add dsh-webhook-dispatcher
Configure endpoints through the WEBHOOK_* env block and/or the cordis
config layer (see Configuration).
Endpoints register as exact routes on the profile's webServer service. The
web profile provides it through the Web GUI server; every other profile
(sdk, acp, a custom bundle) must compose a webServer provider into its
own composition - the framework's route server
@deepseek-ai/dsh-host-webserver (see its own docs). One-shot surfaces such
as headless cannot host a resident listener, so they cannot host this plugin:
# $DSH_HOME/profiles/<name>/cordis.patch.yml (example)
- insert:
- id: webserver
name: '@deepseek-ai/dsh-host-webserver'
config:
host: <host>
port: <port> # a free fixed port; port 0 re-resolves the bound address on every restart
Endpoints are configured through two equal channels that fold onto one
contract: the WEBHOOK_* env block (read once at process boot) and the
cordis config layer (the profile/user cordis.patch.yml entry targeting the
plugin id dsh-webhook-dispatcher; hot - a patch change re-constructs the
plugin entry, which re-reads both channels). Precedence per field: config
layer > env > built-in defaults.
- id: dsh-webhook-dispatcher
config:
endpoints:
- id: <name>
path: /<path>
policy: "Treat the content as a task description and act on it."
# name: <name> (default: the id)
# cwd: /abs/path (default: $DSH_HOME/dsh-webhook-dispatcher)
# auth: <TOKEN_ENV> (env var NAME holding the token value; empty = open endpoint)
# sandbox: workspace-write (read-only | workspace-write | danger-full-access)
# model: some/model (default: the default selection ladder)
# reasoningEffort: low (closed set off|minimal|low|medium|high|xhigh|max; absent: the framework provider default)
# maxConcurrent: 1 (default)
# maxQueue: 100 (default)
# maxBodyBytes: 262144 (default)
# sessionRetention: 24h (session offload window; absent/0 = retain in the store)
Unknown keys are rejected (a typo guard); path must not collide with a
framework-owned route.
The same endpoint shape as WEBHOOK_<ENDPOINT_ID>_<FIELD> variables:
WEBHOOK_<NAME>_PATH=/<path>
WEBHOOK_<NAME>_POLICY="Treat the content as a task description and act on it."
WEBHOOK_<NAME>_AUTH=<TOKEN_ENV> # the NAME of the env holding the token value
WEBHOOK_<NAME>_MODEL=some/model
WEBHOOK_<NAME>_REASONING_EFFORT=low
<TOKEN_ENV>=...
| field | env suffix | built-in default |
|---|---|---|
path (required) | PATH | - |
name | NAME | the id |
policy | POLICY | empty (free-policy fallback) |
cwd | CWD | $DSH_HOME/dsh-webhook-dispatcher |
auth | AUTH | empty (open endpoint) |
sandbox | SANDBOX | the session default |
model | MODEL | the default selection ladder |
reasoningEffort | REASONING_EFFORT | the framework provider default (closed set off/minimal/low/medium/high/xhigh/max, validated at boot) |
maxConcurrent | MAX_CONCURRENT | 1 |
maxQueue | MAX_QUEUE | 100 |
maxBodyBytes | MAX_BODY_BYTES | 262144 |
sessionRetention | SESSION_RETENTION | absent (retain in the store) |
ci-run -> WEBHOOK_CI_RUN_*)PATH, an
out-of-bounds value, or a declared AUTH name that is not set fails the
plugin at boot (fail-closed, the config layer's posture)WEBHOOK_ variable warns at bootwebhook { verb: "status" } - the live endpoint state, the recent-delivery
ring, and the effective (non-secret) endpoint config. See the
dsh-webhook-dispatcher skill for the full reference.
Webhook dispatches leave on-disk session logs in the framework session store;
at delivery volume (frequent or chatty endpoints) the store grows without
bound. The per-minute offload sweep archives settled trigger sessions once
their endpoint's sessionRetention window elapses, so the store stays
bounded: the store is never deleted, only moved, and a session that is still
running is kept until it settles.
sessionRetention is per-endpoint (the config layer's
sessionRetention field or the env
WEBHOOK_<ID>_SESSION_RETENTION): a non-negative integer number of
milliseconds, or a duration string of 1-2 digits+unit segments (units
d/h/m/s/ms) in descending order (e.g. 24h, 4h30m, 90s, 5000ms;
a bare numeric string is invalid). 0 / absent = retain (no offload); an
invalid value warns and is treated as 0 (never a boot failure).WEBHOOK_OFFLOAD_BATCH (env, default 50) bounds the
sessions one sweep may process; an invalid value warns and falls back to
50, never disabling the sweep. When consecutive sweeps observe new log
lines arriving faster than the budget, the sweep logs a trend warning
(raise the knob or lower the retention windows; the warning takes no
action on its own).The offload state (retention log retention-log.jsonl, archive
archived-sessions/, and the default trigger-session workspace) lives under
the plugin home $DSH_HOME/dsh-webhook-dispatcher/. The selftest covers the
offload plane as well (window parsing, retention-log mechanics, the sweep's
window/liveness/budget/trend semantics, the dispatch write gate, the archive
move, and the layout pin).
pnpm install # peers resolve from the registry (onboarding .npmrc / pnpm-workspace.yaml)
pnpm run build # tsc -> lib/
pnpm run typecheck
pnpm run lint # eslint over src/ and test/
pnpm run selftest
Peer resolution is a deliberate dev/staged pair: the dev tree keeps
autoInstallPeers: true so the peer closure resolves from the package's own
node_modules (needed for the in-tree typecheck); the staged deployment tree
ships autoInstallPeers: false plus .npmrc legacy-peer-deps=true, so peers
resolve from the enclosing plugin tree of the deployment, like every
neighbor plugin.