dsh-ab-publish
Publish information to an arbitrary website from a model tool call. The plugin
drives a real browser (Playwright, defaulting to the system Edge channel so
no Chromium download is required) to log in, fill a form, upload files, and submit
— the same capability the agent-browser skill exposes, packaged as a first-class
DSH wire tool.
It is an out-of-tree, npm-publishable plugin: it never edits the deepseek-harness
framework source, mounts through cordis.patch.yml alone, and is installed purely
from the npm registry (no symlink / link:).
Tools
publish_to_site — open url, fill the named form fields, upload any files,
click the submit control, wait for the result, and report status. Reuses a saved
login session so credentials survive daemon restarts.
login_to_site — authenticate once and persist the session (cookies + storage
state) under the workspace; returns a session id the model can reuse.
The two tools are platform-agnostic: any site (Xiaohongshu, Douyin (抖音), or your own
admin form) is driven by a site preset in sitesFile, not by code. Site-specific quirks
that the generic engine cannot express — Xiaohongshu's QR-free phone login and shadow-root
publish button, Douyin's QR-scan login and image-text composer — live as operator toolkits
under src/xhs/ and src/dy/ (see below).
Professional domain handling
- Robust field resolution (
selector.ts): each field key is tried as a CSS
selector, then by id / name / placeholder / aria-label, then by a
<label> whose text contains the key and points at the control. A form with no
stable selectors still fills.
- CAPTCHA / 2FA detection (
detect.ts): before submitting, the page is scanned
for verification challenges (reCAPTCHA, hCaptcha, 验证码, 双重认证, SMS/2FA). When
one is present the call stops with status needs_human instead of burning a
retry on a wall it cannot pass.
- Login persistence (
store.ts): sessions are written under the session
workspace root, so a scheduled publish that runs after the browser daemon restarts
still has its credentials.
- Human-in-the-loop login (
engine.ts): when a login preset marks a field with
the __HUMAN__ sentinel (an SMS code, 2FA, a slider), the engine skips that field,
forces a visible browser, waits for the person to finish, and then saves the session
— so a phone-number login that needs a verification code becomes a one-time manual
step, reused automatically on every later publish_to_site via session.
- Retries with backoff, bounded field/file sizes, and a step log persisted next
to each screenshot for auditability.
Configuration
Every bound is a .required() Schemastery guard, so a deployment states the size it
is willing to carry instead of inheriting a hidden default; omitting any of them
fails activation with $.<field>: missing required value. The install script writes
the deployment's values into the profile's cordis.patch.yml as an id-only row.
| Field | Meaning |
|---|
publishDir | Workspace subdir for sessions, logs, and screenshots. |
maxRetries | Submit attempts before giving up. |
timeoutMs | Per-action timeout (navigation, fill, wait). |
headless | Run the browser without a visible window. |
browserChannel | Playwright channel; default msedge. |
waitNetworkIdleMs | Idle wait after navigation / submit. |
maxFieldBytes | Cap on one field value's length. |
maxFilesPerCall | Most file uploads per call. |
maxFileBytes | Cap on one uploaded file's size. |
Optional: defaultUserAgent, defaultLocale, humanWaitMs (most ms login_to_site
waits for a __HUMAN__ step; defaults to 300000).
Architecture
src/index.ts composition root: Config, Schemastery schema, self-aware
routing gate, agent/pre-step auto-invocation, the two tools
src/engine.ts pure publish pipeline (Playwright), no registry in sight
src/selector.ts field -> control resolution strategies
src/detect.ts CAPTCHA / 2FA / anti-bot challenge detection
src/store.ts persist sessions, step logs, screenshots under the workspace
src/sandbox.ts per-call sandbox fence (workspace-rooted writes)
src/trigger.ts auto-invocation intent detection
src/card.ts PublishCardMeta projector the browser card redraws from
src/client/* browser half: the publish result card
src/xhs/* Xiaohongshu operator toolkit (login / recon / verify scripts)
src/dy/* Douyin operator toolkit (login / recon / verify scripts)
The host and browser halves ship from one package; the browser half is built to the
out-of-tree window.__ModuleLoader__.load contract and the card redraws from the
logged call alone.
Site-specific toolkits
The generic engine carries no site knowledge, so each platform that needs a login or
composer quirk the engine cannot express ships an operator toolkit inside the package:
src/xhs/README.md — Xiaohongshu (小红书): phone-code login, media-first composer,
shadow-root publish button resolved through submitPosition.
src/dy/README.md — Douyin (抖音): QR-scan login (dy-login.mjs), image-text (图文)
composer whose title/caption appear only after upload (handled by the engine's deferred
fill pass), and a douyin-image preset.
Both toolkits are .mjs scripts run by hand (pnpm run xhs:* / pnpm run dy:*); they are
not imported by src/index.ts and tsc never compiles them, so they cannot change how any
other site publishes.
Build & verify
pnpm install
pnpm verify # typecheck && build && test && lint