ricardochen1996/dsh-browser-use ↗★ 1
@weichen96/dsh-browser-use
DeepSeek Harness browser provider: read a page as an indexed action space, then run one guarded operation at a time. 适合需要让Agent通过浏览器执行自动化网页操作任务的用户。
Install
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:ricardochen1996/dsh-browser-useREADME
Read the full README ↗5. Configuration
Change it in the UI (recommended): the plugin exports DSH's Config schema (lib/config.js), and the web half mounts the form in two places, so you don't need to find a config file and changes take effect immediately:
- Sidebar → Plugins → open
@weichen96/dsh-browser-use: the toggles are drawn right above "Included components" (the plugin page's own config area). - On the same page, the
dsh-browser-userow title is itself a "Configure" button (with a>arrow, accessible nameConfigure @weichen96/dsh-browser-use) — it opens the same form.
The form has only the jev group, drawn with the shell's own components in the same row layout as other plugins:
| Field in the form | Meaning |
|---|---|
jev.enabled | Enable browser_goal and browser_act's intent (spends TypeSafe and text-model quota) |
jev.source | session inherits the main conversation; custom uses the URL/key you fill in below |
jev.typesafe.baseURL / .model / .apiKeyEnv / .apiKey | TypeSafe endpoint, model name, key (credential name or plaintext) |
jev.textModel.baseURL / .model / .apiKeyEnv / .apiKey | text-model endpoint, model name, key |
These fields are .volatile(): a change takes effect on the spot (the next browser_goal or intent-bearing browser_act uses the new value); apiKey is submitted with role('secret') and never appears in any form response, and leaving it blank in the form means "don't touch the stored key".
All other switches are changed in the profile patch (after which DSH remounts the plugin, as before): mode, cdpEndpoint, executablePath, userDataDir, headless, allowScreenshots, requestTimeoutMs, delegate, delegateMode, subagentProvider, maxDepth, projectPath, pythonPath, reserveBrowserUseSlot, jev.typesafe.fallbackURL, jev.textModel.reasoning, jev.textModel.headers. They decide which tools exist, which interpreter runs, and whether a composition slot is taken — making them form fields would promise an "immediate effect" that can't be delivered.
One known edge: jev.enabled changes the tool set, and the conversation's own tool surface rebuilds immediately; but an already-running persistent browser subagent keeps the tools it was composed with, until it is swapped with fresh: true or the plugin is remounted (the browser_doctor Jev line always reflects the current config).
After a first pass through the UI, the YAML form below still works (and is the only entry point for fields like projectPath):
Write it into the config: of the id: dsh-browser-use line in the profile's cordis.patch.yml:
- id: dsh-browser-use
config:
# projectPath: /absolute/path/to/jev-ultrafast # only to run an engine checkout instead of the bundled engine
jev:
enabled: true # enable browser_goal and browser_act's intent; when false nothing is resolved and no key is sent
source: session # session: inherit the main conversation; custom: configure it yourself
typesafe:
baseURL: https://opencode.ai/zen/v1/systemone
model: jev-1.13
textModel:
reasoning: thinking-disabled
headers: { x-opencode-session: dsh-harness }
source: session (inherit from the main conversation): on each browser_goal or intent-bearing browser_act, it reads the model route the conversation is currently using (it follows you when you switch models) — the text model uses that route's baseURL, headers, model name and key (the DSH credential the provider config's apiKeyEnv points at); TypeSafe uses the same key, with its endpoint from jev.typesafe.baseURL (the TypeSafe endpoint can't be inferred from a chat route). You can only override typesafe.baseURL/model/fallbackURL, textModel.model (blank = the main conversation's model), textModel.reasoning and appended textModel.headers. It refuses with a fix hint when: the route is the Anthropic protocol, has no baseURL, uses a login session rather than an API key (e.g. a DeepSeek account login), or the conversation hasn't picked a model yet.
source: custom (configure separately):
jev:
enabled: true
source: custom
typesafe:
baseURL: https://opencode.ai/zen/v1/systemone
model: jev-1.13
apiKeyEnv: OPENCODE_GATEWAY_API_KEY # name of a DSH credential (stored on the Models page) or a DSH env var
textModel:
baseURL: https://opencode.ai/zen/go/v1
model: deepseek-flash
reasoning: thinking-disabled
headers: { x-opencode-session: dsh-harness }
apiKeyEnv: OPENCODE_GATEWAY_API_KEY # or apiKey: plaintext (not recommended)
In both modes, the endpoint and key used by browser_goal and browser_act's intent come only from the plugin config / main conversation: the sidecar does not read the engine checkout's .env, and any TYPESAFE_* / TEXT_MODEL_* in the DSH process environment are cleared before the sidecar starts; the key is passed per request to the sidecar and is visible only during that run. browser_doctor shows the jev status and key source (never the key itself). The old allowGoalMode: true is still equivalent to jev.enabled: true.
| Field | Default | Meaning |
|---|---|---|
projectPath | empty (the bundled engine, in this package's .venv, installed automatically) | an engine checkout to run instead; once set, only interpreters that import the engine from here are accepted, and the plugin installs nothing |
pythonPath | auto | pin an interpreter that already imports jev_ultrafast; once set, only it is tried, and the plugin installs nothing |
mode | launch | launch starts a browser; attach connects to a running one |
cdpEndpoint | — | the DevTools address for attach (http(s):// or ws(s)://); blank auto-attaches to the Chrome you use with chrome://inspect remote debugging on |
executablePath | system Chrome | which browser to launch |
userDataDir | one per Session (~/.jev-ultrafast/browser/) | dedicated profile directory |
headless | false | launch without a window |
reserveBrowserUseSlot | true | take the ctx.browserUse singleton slot (when that service is mounted) |
jev.enabled ✎ | false | enable browser_goal and browser_act's intent (spends TypeSafe and text-model quota); old name allowGoalMode |
jev.source ✎ | session | session inherits the main conversation's route and key; custom uses the baseURL/key you configure below |
jev.typesafe | engine default | TypeSafe endpoint: baseURL, model, fallbackURL; add apiKeyEnv / apiKey for custom. The form has the first two plus two key fields; fallbackURL is YAML-only |
jev.textModel | main conversation / engine default | text model (OpenAI-compatible): model, reasoning (none / thinking-disabled), headers; add baseURL, apiKeyEnv / apiKey for custom. The form has baseURL / model plus two key fields; reasoning / headers are YAML-only |
allowScreenshots | true | enable browser_screenshot |
requestTimeoutMs | 180000 | per-request cap for the sidecar |
delegate | true | hand browser operations to a subagent; when false, mount them on this conversation (direct mode) |
delegateMode | persistent | persistent: one persistent browser subagent per conversation, receiving all later tasks; one-shot: one subagent per task, returning after it reports. Falls back to one-shot automatically when the provider doesn't support continuable subagents |
subagentProvider | spawn | the provider name in ctx.subagents; it must be able to compose an in-process subagent, otherwise it falls back to direct mode |
maxDepth | 0 | delegation-depth cap for the subagent; 0 means use the provider's own recursion budget |
✎ = appears in the form under Sidebar → Plugins → @weichen96/dsh-browser-use (the package page, or the "Configure" in the row title), with immediate effect (the next time the value is used); fields without ✎ are set only in the profile patch.