# dsh-client-open-in-vscode [中文](README.md) | English Plugin for the DeepSeek Harness (DSH) web UI: **click a file path in the chat UI to open it in the already-running VS Code window whose workspace matches**. ## Features Clicks on the following elements are intercepted in the capture phase: | UI element | Detection | |------------|-----------| | Tool-card paths (Write/Edit/Read, etc.) | `button.*_fileLink`, text is the path (may be workspace-relative, resolved against the session cwd) | | "Produced files" chips | `[data-produced-files-row] button[title]` | | File mention links in messages | a/button/[role=link] whose `title` is an absolute path | On a hit, `POST /plugin-open-in-vscode` → the host runs `code --goto --reuse-window `; VS Code routes the window by workspace ownership and brings it to the front. **Edit/Write behavior (v0.6)**: clicking the path on a file-change row (Edit/Write, etc.) opens a **full-file diff of that exact edit**. It works like reverse `git apply`: the row's payload carries the edit's before/after hunks (change ± context lines); stitching the hunks back into a complete file yields both full versions. It first tries the **current file on disk** (covers uncommitted and committed edits — a committed file already contains the post-edit text), then the **git HEAD version** (covers edits whose target was later reverted/rewritten); if either base contains the hunks and matches uniquely, the reconstruction succeeds and display still goes through standard `code --diff`. When reconstruction is impossible (later commits rewrote the same region), it falls back to an inline hunk diff; **in-flight/failed** calls also use the hunk preview since the change has not landed on disk. Temp files are cleaned up after 10 minutes; rows without diff data (Read/Bash, etc.) still open directly. > Historical edits diff correctly too: committed edits are reconstructed from the committed content (the v0.5 HEAD-vs-worktree approach showed "no changes" for committed edits and is deprecated). In multi-window setups both diff sides are temp files and window routing falls back to the **most recently active** window of the same authority. Multi-window routing (VS Code window-manager behavior): candidate windows are first filtered by remote authority (only windows connected to the same host/local), then by **which opened workspace folder contains the file's absolute path** — multi-root workspaces take the first root in declaration order; when several single-folder windows contain the file, the **deepest** path wins; with no match at all it falls back to the **most recently active** window of the same authority. The session cwd is only used to complete relative paths into absolute ones; it plays no role in window matching. - **Alt+click**: bypasses the interception and uses the product's native behavior - A toast at the bottom of the page reports the outcome (success/failure) ## Security The route uses the same trust fence as `/api`: Host must be loopback (DNS-rebinding protection), `Sec-Fetch-Site: cross-site` is rejected, and when `Origin` is present it must match the Host (drive-by POST protection). That means **other websites cannot use your browser to make local VS Code open files**; paths must be absolute and never pass through a shell (`execFile` with an argument array). ## Why not `vscode://file/...` v0.1 jumped to the `vscode://` protocol via an anchor — fine in a real browser, but the DSH GUI often runs inside a **VS Code webview**, and a webview navigating to that protocol **black-screens**. Since v0.2 the host side uses `code --goto`, which is webview-safe. ## Install Assuming `DSH_HOME` is `~/.dsh` (the default): 1. Copy the package into the web profile's node_modules: ```bash cp -r dsh-client-open-in-vscode ~/.dsh/profiles/web/node_modules/ ``` 2. Edit `~/.dsh/profiles/web/cordis.patch.yml` and add: ```yaml - insert: - id: ui-open-in-vscode name: dsh-client-open-in-vscode ``` 3. Restart `dsh web` (the host half registers its HTTP route at startup — a page refresh alone is not enough). Optionally verify the composition: ```bash dsh --profile web --dump-config | grep ui-open-in-vscode ``` ## Requirements - The `code` CLI on PATH (ships with standard VS Code installs) - A desktop session (X11/Wayland) so the host process can raise GUI windows ## Uninstall 1. Remove the `- insert:` block from `cordis.patch.yml`; 2. Delete `~/.dsh/profiles/web/node_modules/dsh-client-open-in-vscode`; 3. Restart `dsh web`. ## Files | File | Purpose | |------|---------| | `index.js` | Host half: registers the `POST /plugin-open-in-vscode` route (loopback/Origin trust fence), `execFile('code', ['--goto','--reuse-window', path])` | | `client.js` | Browser half: click interception + fetch POST + toast (`window.__ModuleLoader__` format) | | `cordis.patch.yml` | Plugin insert config (needed for `dsh plugin add` / marketplace install) | | `package.json` | Package manifest with `dsh.bundle` + `dsh.client.platform: "web"` declarations | Compatibility: tested against the web profile of `@deepseek-ai/dsh` 0.1.0-rc.6. If product DOM classes/hashes change in a new version, the selectors in `targetPath()` inside `client.js` need updating.