← Back to home@dragonTalon

dsh-browser-assistant

将dsh转成谷歌插件

Stars
1
Language
TypeScript
Created
Sep 4, 2026
Updated
Sep 19, 2026

Introduction

bridge-browser icon: an orange round-faced character wearing a monocle

dsh Browser Assistant

English | 中文

npm version extension version

Let DeepSeek Harness (dsh) read and operate the browser tab you already have open — pages become text-only structured snapshots, the model addresses elements by number, and your login state, session, and cookies stay intact.

One pnpm workspace, two halves joined by one WebSocket:

  • packages/bridge-dsh — the dsh Cordis plugin, released as bridge-dsh 0.2.0, that mounts /ext/bridge and registers 12 browser_* tools.
  • packages/extension — the Chrome MV3 extension, released as bridge-browser 0.2.0 (service worker + content script + side panel).

The model tool pipeline is text-only — pages become structured text snapshots, tools never capture screenshots. Separately, a user-initiated drag-select in the panel can send a cropped region screenshot to a vision-capable model. See docs/en/architecture.md for the full design.

Capabilities

CapabilityHow
Read pagebrowser_snapshot → title/URL/main text/numbered controls/forms; delta:true returns only changes
Operatebrowser_click / browser_type / browser_press / browser_scroll by stable number
Navigatebrowser_navigate / browser_open_tab / browser_back / browser_forward / browser_reload
Read region / waitbrowser_get_text / browser_wait
Ask the userdsh ask_user_question renders in the panel; answers flow back to the model
Page awarenessextension tracks the active tab and injects its URL/title into each prompt as context
Region captureuser drag-selects a page region → cropped screenshot + DOM element list → sent to a vision-capable model
Model selectionpanel re-pulls model.catalog on connect; dropdown with capability badge (vision / text / unknown) → session.selectModel
Session pickerdropdown over session.list, "new session" by default; a session is created on the first send, on the first model pick, or when the / menu is opened from "new session". Picking a past session binds it and replays its history — no orphans, no session.create
Slash commands & skills/ opens a filtered, keyboard-navigable menu over the bound session's host commands and user-invocable skills: a command runs through commands.execute (whole line, arguments included), a skill is sent as an ordinary prompt, and the command/run/command/done pair renders its lifecycle in the conversation
Session groupingwith sessionWorkspace configured, extension-created sessions join that dsh Workspace — grouped under the directory in the dsh sidebar instead of piling up in "Ungrouped"

Security model: the bridge carries its own bearer token; reads are auto-allowed, state-changing actions fail closed behind a side-panel approval; passwords/card numbers are masked and never leave the page. A remote connection requires the token and relaxes none of the above.

Requirements

  • Node.js ^22 and Corepack/pnpm
  • dsh ≥ 0.1.2-rc.1 — the minimum supported version (the 0.1.x Typert Gateway + Connection architecture); verified against 0.1.3-alpha.1
  • Chrome 116+

Install

The bridge plugin is published to npm; the Chrome extension ships as a pre-built zip on GitHub Releases. No local compilation needed.

1. Install the bridge plugin (from npm)

dsh plugin --profile web add -w "bridge-dsh@0.2.0" --config.minimumReleaseAge=0

Pin the version — do not use @latest. Since pnpm 11, minimumReleaseAge defaults to 1440 minutes (1 day): a version published less than a day ago is held back, and a dist-tag like @latest silently resolves to the previous version instead of failing. --config.minimumReleaseAge=0 lifts that wait for this one install. The bridge plugin and the extension are a versioned pair, so always install the version that matches your bridge-browser zip.

2. Download the Chrome extension

gh release download bridge-browser@0.2.0 --repo dragonTalon/dsh-browser-assistant
# → bridge-browser-0.2.0.zip

3. Restart dsh and verify

cd ~/.dsh && dsh web
curl http://127.0.0.1:3080/ext/bridge-config
# → {"wsUrl":"ws://127.0.0.1:3080/ext/bridge"}

4. Load the extension

Unzip bridge-browser-0.2.0.zip, then chrome://extensions → enable Developer mode → Load unpacked → select the unzipped folder. Open any http(s) page, click the extension icon to open the side panel, wait for 已连接 dsh, and chat.

5. Remote dsh (optional)

If dsh runs on another machine, open the panel's System config (gear button in the status bar), enter the address (10.0.0.7:3080 or wss://dsh.example.com) and the token from that machine (cat ~/.dsh/ext-bridge-token), then press Test connection before saving — it performs one isolated handshake and tells you whether the address is unreachable or the token was rejected. Leaving the address empty keeps the zero-config local discovery.

Methods pinned to loopback (settings.*, credentials.*, host.openPath, host.pickDirectory) stay unavailable over a remote connection — that is the bridge's own trust fence, not a bug in the dialog.

Build from source (optional)

pnpm install --frozen-lockfile
pnpm build
# → packages/bridge-dsh/lib/index.js  and  packages/extension/dist/

Note for local development: dsh plugin add installs a packed snapshot (~/.dsh/profiles/<profile>/node_modules/bridge-dsh/), so rebuilding the workspace lib/index.js alone never reaches the running dsh. After every bridge source change:

bash packages/bridge-dsh/build.sh       # rebuild the workspace artifact
bash scripts/sync-profile.sh            # copy into the installed plugin dir (auto-backup)
# then restart dsh (or reload the plugin) so the new bundle is loaded

The copy above replaces the profile's file, but the running dsh keeps the module it already imported. A live patch reload re-runs the plugin's apply() with the new config while still using the old module, so a source change always needs a dsh restart.

Checks

pnpm test                       # the release gate: typecheck + ALL offline checks (scripts/run-tests.mjs)
pnpm test:e2e                   # the two live checks in one command; needs a running dsh
pnpm check:slash                # 28 offline assertions for the slash vocabulary + catalog lifecycle
pnpm check:ordered-rpc          # 7 assertions driving a real BridgeServer over a real WebSocket:
                                #   the ordered-RPC bound releases the queue slot instead of hanging the session
pnpm check:grouping-lifecycle   # 6 assertions: workspace registration survives a connection replacement
pnpm check:selection            # 32 offline assertions for the session picker (buffers, seq, history replay)
pnpm check:grouping             # 24 offline assertions for the sessionWorkspace contract + its diagnostic trace + plugin wiring
pnpm check:permission           # 43 offline assertions for the permission-tier rules + spec-scenario coverage classification
pnpm check:grouping:status      # read-only triage (no dsh needed): registry membership vs the directory's real Session files
pnpm check:selection:e2e        # live end-to-end: session selection against a running dsh
pnpm check:grouping:e2e         # live end-to-end: session.create over the real bridge, asserted from the Workspace registry

The offline checks need no dsh, no Chrome and no network — they bundle the real sources with esbuild and assert the spec scenarios in Node; pnpm test runs them all (plus typecheck) through scripts/run-tests.mjs, and is the gate every release must pass. The bridge's own typecheck needs the dsh host framework's types (@deepseek-ai/*), which live in a local dsh installation: after checkout run bash scripts/link-dsh-types.sh once to link them into the package. check:grouping:status reads only the on-disk registry and Session store, and answers "why did this conversation not land in the group?"; the two :e2e checks need a running dsh (check:grouping:e2e additionally needs sessionWorkspace configured); they briefly supersede the Chrome panel's bridge connection (the bridge serves one at a time; the extension reconnects on its own) and create a real Session on every run.

Releases

The two halves are released independently:

ArtifactPackageVersionGit tag
dsh bridge pluginbridge-dsh0.3.0bridge-dsh@0.3.0
Chrome extensionbridge-browser0.3.0bridge-browser@0.3.0

Tagging is done through the release gate, which refuses to tag unless the full offline suite passes:

bash scripts/tag-release.sh bridge-dsh 0.3.0      # typecheck + pnpm test, then tags and pushes
bash scripts/tag-release.sh bridge-browser 0.3.0 --e2e   # additionally runs the live checks (needs a running dsh)

The script verifies the version against the package's package.json (and the extension's manifest.json), runs the gate, then creates and pushes the tag; any failure aborts without a tag. The pushed tag triggers the pipeline below, which runs the same offline suite once more before building.

The bridge plugin is on npm: bridge-dsh. Each tag also has a matching GitHub Release with its built artifact, produced automatically by the tag-triggered pipeline (.github/workflows/release.yml):

  • bridge-dsh — install from npm: dsh plugin --profile web add -w "bridge-dsh@0.3.0" --config.minimumReleaseAge=0 (a bridge-dsh-0.3.0.tgz is also attached to its release)
  • bridge-browser-0.3.0.zip — the extension bundle; load it via chrome://extensions → Load unpacked (or submit to the Chrome Web Store)

Every release description is bilingual and is generated from that package's changelog, so the notes can never drift from what shipped: packages/bridge-dsh/CHANGELOG.md · packages/extension/CHANGELOG.md.

Repository layout

packages/protocol/     shared zero-dependency wire protocol (single source of truth)
packages/bridge-dsh/   dsh bridge plugin (Cordis)
packages/extension/    Chrome MV3 extension (background / content / panel)
docs/en/ docs/zh/       architecture & feature docs (EN / 中文)

Docs

Documentation is bilingual — every page has an EN | 中文 switcher: