DSH Bridge

by sky
5
4
3
2
1
Score: 31/100

Description

DSH(DeepSeek Harness)嵌入 Obsidian 的 AI 协作者插件:聊天侧边栏、内联编辑、@提及与计划模式(连接本地 http://127.0.0.1:3080)

Reviews

No reviews yet.

Stats

4
stars
503
downloads
0
forks
49
days
3
days
3
days
0
total PRs
0
open PRs
0
closed PRs
0
merged PRs
3
total issues
3
open issues
0
closed issues
125
commits

Latest Version

3 days ago

Changelog

本轮聚焦三件事:补上端口自动探测、真正修好 0.1.8 声称已修但从未生效的状态栏缺陷、完成 DSH 0.2 兼容性核查。

新增

  • 端口自动探测(重要):DSH 桌面 App 固定监听 19387,dsh web 命令默认 3080,而本插件的默认值是 3080—— 用桌面 App 的人不改设置必然连不上,而诊断只会说「DSH 似乎没有在运行」,完全指错方向。 现在插件启动时会自动探测这两个入口(桌面优先),命中就写回设置并弹一条通知,一般不需要手填地址。

    规则上刻意克制了两处:配置地址能连上就尊重它(手填的地址可能是刻意的,比如就是要连 CLI 那个实例); 只有本地回环地址才参与探测,远端地址不会被本地端口顶掉。

    判据是一次真实的会话列表调用——同时穿过认证与 RPC 两层,不是 TCP 连通性,端口上蹲着别的程序也不会误判。

  • 设置 →「诊断连接」失败后也走同一条探测,覆盖「Obsidian 先起、DSH 后起」的盲区(那时启动探测什么也探不到)。

修复

  • 状态栏「DSH 未运行(连接被拒绝)」从未显示过(重要):0.1.8 的发布说明里写了这条能力,但它实际从未生效。

    根因是时序竞态:连接断开时先通知「重连中…」,随后才拿到 ECONNREFUSED 标记;而状态值没有变化, 这次通知被去重逻辑吞掉,于是状态栏永远停在含糊的「重连中…」——恰好是「为什么连不上」最该说清楚的那句。

    现已在该标记翻转时强制再通知一次。真机复验:断联后约 5 秒状态栏正确改口(修复前同样操作 35 秒仍是「重连中…」)。 新增回归用例在撤掉修复时确实会失败,不是那种永远绿的测试。

兼容性

  • DSH 0.2 已验证:对本机 0.2.0-rc.2 逐条核对连接契约(认证 cookie、RPC 信封、端点与参数名、WebSocket 帧), 并用插件自身代码对运行中的服务做真机验证——18 个端点与 $events / session/control / session/follow 三条流全部通过。0.2 未改认证、RPC 信封与 WS 帧协议,升级 0.2 无需为本插件做任何适配。

    逐条核对记录、验证边界与手工验收清单见仓库 docs/dsh-0.2-compat-audit-2026-10-01.md。

  • 端口提示:桌面 App 19387 / dsh web 3080。此前 README 只写了 3080,容易把桌面用户带偏。

文档

  • README 兼容性矩阵新增 0.2 行,并把「比已验证版本更新的 DSH」改为「比 0.2 更新(0.3+)」。
  • 前置条件与安装步骤写明两种启动方式分别对应哪个端口。

验证

  • npm test → 567 个测试 / 38 个文件全绿(较 0.1.8 +11);npm run lint → 0 问题; npm run build → 通过,且连跑两次 main.js 字节一致(构建可复现)。
  • 全部改动在真实 Obsidian 1.13.7 + 本机 DSH 0.2.0-rc.2 上真机验收通过: @提及、流式输出、审批写文件、内联编辑词级 diff、端口自动探测(含「故意填错端口」与「两个入口都活着时选桌面端」)、 断联与恢复(恢复耗时 0.13 秒)。
  • 断联测试用一个本地 TCP 代理造出可控入口,避免杀掉承载会话的 DSH 本身;代理透传 Host 头, 因此插件按 authority 签名的 cookie 仍走真实校验路径。

English

This round focuses on three things: port auto-detection, a status-bar fix that 0.1.8 claimed but never actually delivered, and the DSH 0.2 compatibility audit.

Added

  • Port auto-detection (important): the DSH desktop app always listens on 19387, the dsh web command defaults to 3080, while this plugin shipped with 3080 as its default — so desktop users who never touched the setting could not connect, and the diagnosis only said "DSH does not appear to be running", pointing in the wrong direction entirely. The plugin now probes both entries on startup (desktop first), writes the working one back to settings, and shows a notice. You normally don't need to type an address.

    Two deliberate restraints: a working configured address is left alone (a hand-typed address may be intentional, e.g. you really do want the CLI instance), and only loopback addresses take part in probing — a remote address is never replaced by a local port.

    The check is a real session-list call — it passes through both authentication and the RPC layer, so it is not mere TCP reachability and cannot be fooled by some other program squatting on the port.

  • The settings "Diagnose connection" button now runs the same probe when it fails, covering the case where Obsidian starts before DSH (at which point the startup probe has nothing to find).

Fixed

  • The status bar never actually showed "DSH 未运行(连接被拒绝)" (important): the 0.1.8 notes advertised this, but it never worked.

    The root cause is a timing race: on disconnect the transport first reports "reconnecting", and only afterwards learns the ECONNREFUSED reason — by which time the state value has not changed, so that notification is swallowed by the de-duplication and the status bar stays on the vague "重连中…" forever. That is exactly the sentence that matters most when someone asks "why can't I connect?".

    The fix re-notifies when that flag flips. Verified on a real machine: the status bar corrects itself about 5 seconds after the entry dies (before the fix, the same test still read "reconnecting" after 35 seconds). The new regression test genuinely fails when the fix is removed.

Compatibility

  • DSH 0.2 verified: every connection contract (auth cookie, RPC envelope, endpoints and argument names, WebSocket frames) was checked against 0.2.0-rc.2 on this machine, and the plugin's own code was run against the live server — all 18 endpoints and the $events / session/control / session/follow streams pass. 0.2 changed neither authentication, nor the RPC envelope, nor the WS frame protocol, so upgrading to 0.2 needs no adaptation for this plugin.

    The per-item record, verification boundaries and manual acceptance checklist live in docs/dsh-0.2-compat-audit-2026-10-01.md.

  • Port note: desktop app 19387, dsh web 3080. The README previously only mentioned 3080, which misled desktop users.

Docs

  • README compatibility matrix gained a 0.2 row; "newer than the verified line" became "newer than 0.2 (0.3+)".
  • Prerequisites and install steps now state which port belongs to which way of starting DSH.

Verification

  • npm test → 567 tests / 38 files green (+11 vs 0.1.8); npm run lint → clean; npm run build → passes, and two consecutive builds produce a byte-identical main.js.
  • Everything was accepted on a real Obsidian 1.13.7 + DSH 0.2.0-rc.2: @-mentions, streaming output, approval-gated file writes, inline-edit word-level diff, port auto-detection (including "deliberately wrong port" and "both entries alive → desktop wins"), and disconnect/recovery (recovery took 0.13 s).
  • The disconnect test used a local TCP proxy as a controllable entry so the DSH hosting the session was never killed; the proxy forwards the Host header verbatim, so the plugin's authority-bound cookie still went through real validation.

README file from

Github

DSH Bridge

Embed your locally running DeepSeek Harness (DSH) into Obsidian as an AI collaborator: your vault becomes its working directory, and DSH can read, write, and search your notes directly.

中文文档

Why this plugin

Most Obsidian ↔ agent bridges wrap a web UI or shell out to a CLI. This one is a native client: it speaks DSH's own RPC and event stream, then renders everything with Obsidian's own UI.

  • Native, not embedded — chat, tool cards, approvals and plan mode are real Obsidian UI: your theme, your fonts, your hotkeys.
  • Inline edit with a word-level diff — select text, give an instruction, review the diff, apply; Cmd+Z undoes it.
  • Approvals land where you are — DSH's write/exec confirmations and its questions appear inside the panel, no window switching, and they survive a reconnect.
  • Connection diagnosis — 401 / 404 / connection-refused are translated into an actionable conclusion (Settings → Diagnose connection).
  • Engineered to last — 550+ unit tests, strict TypeScript build, byte-reproducible artifacts, listed in the community directory.

The plugin is a client, not a runtime: it needs a local DSH to talk to (see Prerequisites). If something looks wrong, the diagnose button will tell you which of the two it is.

Prerequisites

  • A running DSH instance on your machine. The port depends on how you start it: the desktop app pins http://127.0.0.1:19387, the dsh web command defaults to http://127.0.0.1:3080. On startup the plugin auto-detects both (desktop first) and writes the working one back to settings — you normally don't need to type an address
  • Your vault must be inside DSH's accessible directory scope (decided by DSH's sandbox / workspace config)
  • Obsidian ≥ 1.7.2, desktop only

Features

Conversation

  • Chat sidebar — streamed responses, tool-call cards, session switching and creation, "load older" pagination, and automatic re-sync after reconnects
  • Approval & question popups — retryable, grouped per session, replayed after a reconnect
  • Thinking process — collapsible reasoning block above each reply, streamed live and folded once the turn completes
  • Image attachments — attach images from your vault to a prompt; the agent reads them directly

Editing your notes

  • Inline edit — select text + hotkey → instruction → word-level diff preview → apply (editor selection is re-validated before applying; large selections degrade to a plain confirm dialog)
  • @mentions — type @ to pick vault files (@file:path, content injected) or folders (@folder:path, directory tree injected), with truncation and missing-file notices
  • Slash commands & plan mode — commands come from the running DSH (so the list always matches your install), plus the local /clear; Shift+Tab toggles plan mode with a status banner

Context & control

  • Model & reasoning effort — pick provider/model and reasoning effort from the panel; the list is grouped from your DSH model catalog
  • Context usage — a status line showing projected tokens against the context window, plus output tokens
  • Todo list — the agent's live todo list with pending / in-progress / completed states
  • Goal panel — view and control a long-running goal (create / pause / resume / complete / clear)

Long sessions stay bounded: when DSH compacts history, replaced messages collapse into the summary instead of piling up.

Screenshots

Chat sidebar @ Mention picker
Chat sidebar with streamed conversation @ mention file picker
Inline edit diff preview Approval popup
Inline edit word-level diff preview DSH tool approval popup
Thinking process & context usage Todos & goal
Collapsible reasoning block while streaming, with the context/usage status line Live todo list with three states and the goal bar
Model & reasoning effort Connection diagnosis
Model dropdown grouped by provider, next to the reasoning-effort selector Settings → Diagnose connection reporting a healthy connection

Installation (Community Plugins)

  1. Settings → Third-party plugins → Browse → search DSH Bridge → Install → Enable (desktop only)
  2. Make sure DSH is running locally and the plugin's DSH address matches it: http://127.0.0.1:19387 for the desktop app, http://127.0.0.1:3080 for dsh web

Prefer a manual install? Grab the latest artifacts from the GitHub releases page and extract them into vault/.obsidian/plugins/dsh-bridge/.

Installation (local / dev)

  1. npm install && npm run build
  2. Copy main.js, manifest.json, and styles.css into vault/.obsidian/plugins/dsh-bridge/
  3. Settings → Community plugins → enable "DSH Bridge"

Privacy

All data flows through your local DSH to its configured model providers, using the same policy as the DSH Web GUI. The plugin sends no telemetry.

Translations / Localization

The plugin ships with all UI strings in a key-value table (built-in default: Chinese). To switch the UI to another language:

  1. In the plugin settings tab, click Export i18n template — this creates dsh-bridge.i18n.json at the root of your vault (visible in Obsidian's file explorer).
  2. Replace the values with your translations (or hand the file to your local DSH / any translator).
  3. Reload Obsidian (or disable/enable the plugin) to apply — repeatable.

The vault-root file takes priority; a legacy i18n.json inside the plugin directory (.obsidian/plugins/dsh-bridge/) is still read as a fallback. Missing keys or invalid JSON silently fall back to the built-in defaults. In v0.1.x, model-facing instructions (inline-edit prompt, @mention expansion) intentionally remain in Chinese; the UI-only string table is safe to translate.

Development

npm install
npm run dev    # watch build
npm test       # unit tests

Architecture

Transport: unary RPC over Node http (POST /api/<namespace>/<method> with {args} payload + self-signed browser-session cookie); live streams over a bundled ws WebSocket (/api/remote.mux — session/follow, session/control, $events). A core layer folds session events into view models; a UI layer renders the sidebar and modals.

DSH compatibility

DSH version line Plugin version Status
0.2 line (verified on 0.2.0-rc.2) 0.1.8+ (current) ✅ Protocol-layer verification and in-Obsidian UI acceptance both pass (2026-10-01): the plugin's own code connected to a live 0.2.0-rc.2 over real HTTP + WebSocket — authentication, all 18 RPC endpoints, and the $events / session/control / session/follow streams pass; and a real Obsidian 1.13.7 accepted @-mentions, streaming output, approval-gated file writes, inline-edit diff, and port auto-detection. 0.2 did not change auth, the RPC envelope, or the WS frame protocol, so no upgrade work is needed. The one gotcha is the port: the desktop app pins 19387, dsh web defaults to 3080 — the plugin auto-detects it (desktop first), so you normally don't set it by hand. Per-item record and verification boundaries: docs/dsh-0.2-compat-audit-2026-10-01.md
0.1.5 line (verified on 0.1.5-rc.1) 0.1.6+ (incl. 0.1.7) ✅ Verified end to end on a real vault (2026-09-13): streamed output, approvals, inline-edit diff, reconnect
0.1.2 line (0.1.2-rc.1, 0.1.2) 0.1.5+ ✅ Supported — the contract this plugin was built against. 0.1.6+ keeps it working through capability probing: the 0.1.5 streaming channel is requested field by field and dropped automatically if the server rejects it. Verified on a real machine at plugin 0.1.5, covered by unit tests since
before 0.1.2 (e.g. 0.1.0-rc.6) ≤ 0.1.4 ❌ Not supported — returns 401/404. Upgrade DSH, or stay on plugin 0.1.4
newer than 0.2 (0.3+) latest plugin ⚠️ Unverified — DSH ships often and has already changed this plugin's contract twice (0.1.2 → 0.1.5). If the panel breaks after a DSH upgrade, check for a plugin update first

Direct filesystem access (disclosed for community review): DSH's browser-session authentication requires reading the signing secret from ~/.dsh/.credentials.yaml (the DSH process's credentials store, outside the vault). The plugin reads this file read-only — it never writes, never logs its contents, and only uses the secret to sign the per-request cookie required by DSH's browser-session API (0.1.2-rc.1 onwards). The vault API cannot reach this path (it is outside the vault root), so Node fs is required for this one purpose.

  • obsidian-project-management — the Obsidian-based project management skill that governs this plugin's development workflow (project records are tracked in a local Obsidian vault).