DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

Plonkloving /

Plonkloving/dsh-browser

Verified

Tabbed browser panel for the DeepSeek Harness Web UI: DeepSeek search, workspace-bound profiles, declarative extensions, sandboxed page rendering, downloads.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@bfc40aec

dsh-browser

A browser panel for the DeepSeek Harness Web UI. It is a Cordis bundle: a Host half that adds fenced HTTP routes and one agent tool, and a web Client half that registers a browser main panel plus a rail icon.

中文说明见 README.zh.md.

Install

dsh plugin add Plonkloving/dsh-browser

The package declares dsh.bundle (cordis.patch.yml inserts one Host row) and dsh.client (the web half). There is no build step and no runtime dependency: the Client bundle is plain JavaScript loaded through window.__ModuleLoader__.

After installing, the rail icon (a globe, after Plugins and Schedules) opens the panel.

Host-side edits need a row reload — disable and re-enable the plugin row, or restart the Harness: the Loader keeps the imported ES module. Client edits reload through HMR.

What it does

Tabs and the omnibox

  • Back / forward / reload / home, a tab strip, and an omnibox that treats input as a URL, a workspace file path, or a search query.
  • Ctrl/⌘-click, middle-click and Alt+Enter open a new tab (Shift keeps it in the foreground). The same intent is reported from inside rendered pages.
  • Browser state — profiles, tabs, bookmarks, history, extension switches, download records — is stored in this origin's localStorage under dsh-browser.state.v1.

Search

The omnibox calls POST /dshbrowse/api/search, which runs ctx.web.search through the Harness web seam (the configured provider, deepseek-official, uses the native web_search server tool). The panel renders the returned sources[] natively and can export the list as CSV or JSON. Results are cached per query for the session, so the same search does not cost a second model call.

Browsing profiles bound to folders

Each profile owns its own tabs, bookmarks, history and extension switches. Binding a folder to a profile also registers it as a Harness workspace (workspaces.create({ path })), after which:

  • New Agent session opens a session in that folder;
  • the page action Send to Agent drafts a prompt from the page title, description and headings into a session in that workspace, then switches to the conversation.

Page rendering

Two views share one tab:

View How
Page (default) GET /dshbrowse/api/render?url=… fetches the page with ctx.web.fetch, injects a <base> pointing at the real site so its CSS, images and fonts resolve, strips the site's scripts and its meta CSP / refresh, appends a navigation bridge, and returns the document from the panel's own origin. The frame is sandbox="allow-scripts allow-downloads allow-modals" — no allow-same-origin, so the document keeps an opaque origin.
Reader GET /dshbrowse/api/page returns the same fetch verbatim; the client parses it with DOMParser into a document model (h1–h6, p, li, pre, img, table, links) that extensions can rewrite.

Because the framed document is served from the panel origin, a site's X-Frame-Options / frame-ancestors no longer applies. The trade-off is that site JavaScript is stripped: a client-rendered SPA may need Reader instead.

Workspace files

Files the Harness writes into a workspace can be opened directly:

  • GET /dshbrowse/api/files?dir=… lists one directory; GET /dshbrowse/api/file?path=… serves one file. HTML/CSS are rewritten so a generated report keeps its stylesheet, images and scripts; everything else is streamed with the right content type and no-cache.
  • Root paths only: a canonical path outside every registered workspace directory (and the downloads folder) is refused with 403.
  • HTML, SVG/PNG/JPEG/WebP, PDF, Markdown, plain text, JSON/CSV, video and audio are rendered inline; the new-tab page and a dedicated file browser list them.
  • POST /dshbrowse/api/inbox consumers: the browser_open tool queues a URL or a workspace file and the panel opens it in a tab.

Extensions

A Chrome-style, declarative extension system:

Field Meaning
permissions shown in the manager, informational
contentScripts[].matches *, *://host/* patterns decide where a script applies
contentScripts[].hide CSS selectors removed before the document model is built
contentScripts[].highlight(s) words marked in the reader
contentScripts[].width reader width
action gives the extension a toolbar button and a panel

Extensions cannot execute code. Built in: ad/tracker cleanup, keyword highlighting, focus reading (paragraph limit), page outline. The manager installs from a pasted manifest.json, enables/disables, removes, and offers three suggested manifests.

Downloads

Everything is written to a real folder, never overwritten (note (1).txt):

Entry point Product
Page → download page HTML (relative references rewritten to absolute URLs), article Markdown, article plain text
File page → download the workspace file; a copied HTML page has its relative references rewritten to file:/// paths so it still renders
Search results CSV or JSON of the source list
Settings one JSON with profiles, bookmarks, history, extensions and settings

The target is $DSH_BROWSER_DOWNLOAD_DIR, else the system Downloads folder (Downloads, 下载, downloads). That folder is also a browse root, so a saved file can be reopened in a tab.

Routes

Route Purpose
POST /search ctx.web.search
GET /page ctx.web.fetch, raw body
GET /render fetched page as a framed, script-free document
GET /files list a directory inside an allowed root
GET /file serve/stream one allowed file
POST /download write {kind: file | url | text} to the Downloads folder
GET /inbox queued browser_open requests

All of them hang off /dshbrowse/api and run behind the Harness browser-trust fence (ctx.connection.requestRejection), the same check the built-in /api channel uses. Nothing is proxied through a third party.

Security model

  • Every route is refused unless the request passes ctx.connection.requestRejection.
  • Path admission: realpath the request, then require containment in a registered workspace directory or the downloads folder; traversal and symlink escapes answer 403.
  • Rendered and local pages run in a sandboxed frame without allow-same-origin, so their markup cannot reach the Harness API even though it is served from the panel origin. Proxied pages additionally have their scripts stripped.
  • Extension manifests only contribute selectors, words and a width; there is no code execution path.
  • Download file names are sanitized and writes never overwrite.

Layout

dsh-browser/
├── package.json        dsh.bundle.patch + dsh.client
├── cordis.patch.yml    inserts one Host row (id: dsh-browser)
├── index.js            Host: routes + browser_open tool
├── lib/files.js        path admission, listing, HTML/CSS rewriting, streaming
├── lib/downloads.js    downloads folder, name sanitizing, unique targets
├── client.js           Client: rail icon + browser panel (UI, document model, extensions)
├── locale/zh.json, en.json
└── icon.svg

Compatibility

  • Host services: web, webServer, connection (required), tools (optional — enables browser_open).
  • Client injections: @deepseek-ai/dsh-client-ui-layout (declares main and sidebar.panellist), @deepseek-ai/dsh-client-locale.
  • No dependencies; the official @deepseek-ai/* packages resolve from the installation.

License

MIT — see LICENSE.

—/ 5

No ratings yet

Verified DSH bundle

Commit bfc40aec660c

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout