Skip to content
dsh.fish
Bundle

dsh-file-path

DeepSeek Harness plugin: drag unsupported files into the composer as workspace path references

Source
dkjsiogu
License
MIT
Updated
Updated 7 days ago

Readme

# dsh-file-path

[中文说明](./README.zh-CN.md)

A DeepSeek Harness (`dsh`) plugin that lets the Web composer accept files and
folders the built-in attachment path does not support — PDFs, archives, Office
documents, large files, whole directories — by inserting their **absolute
paths** as plain text instead of uploading them as message attachments.

This is a community plugin. It is not an official DeepSeek plugin and is not
affiliated with or endorsed by DeepSeek.

## Why

dsh's version-one attachment path accepts PNG, JPEG, WebP, and GIF only.
Dropping a PDF into the composer currently announces "only images are
supported" and drops the file. Browsers do not expose the absolute host path of
a dragged `File` for security reasons, so a web page cannot read the path out
of the drag event — even when the page is served by a local CLI.

`dsh-file-path` recovers the original path using the local dsh host itself:

1. You drag or paste a non-image file into the composer.
2. The browser half computes the file's SHA-256 and calls the local
   `filePathBridge/resolvePath` RPC on the dsh process.
3. The host half searches the session workspace and configured roots
   (`~` by default) for a same-name, same-size, same-hash file and returns its
   **original absolute path**. Nothing is copied or written.
4. The composer inserts that absolute path directly. If no original is found
   (for example the file lives on another drive outside the search roots), a
   dialog asks for the absolute path manually, with an explicit
   "Copy to workspace" fallback available as a second action — never automatic.

A paperclip button in the composer tool row also opens a dialog for manually
typing an absolute or workspace-relative path.

## Install

Pack and install the built tarball:

```sh
npm run build
npm pack
dsh plugin --profile web add ./dsh-file-path-0.1.x.tgz
```

Or install a published version:

```sh
dsh plugin --profile web add dsh-file-path
```

Or install from git (the `prepare` script builds the artifacts; pnpm ≥10 asks
you to allow that build once, and dsh prints the exact `allowBuilds` snippet):

```sh
dsh plugin --profile web add github:<you>/dsh-file-path#<commit-sha>
```

Then boot dsh web as usual:

```sh
dsh --profile web --port 3080
```

> The Web profile already contains the Web app bundle. For a custom profile,
> make sure `@deepseek-ai/dsh-web-app` is in `dsh.profile.bundles` before
> `dsh-file-path`.

## Usage

- **Drag or paste** a non-image file anywhere on the page. The plugin looks up
  its original absolute path by name + size + head/tail SHA-256 samples
  (64KB each) and inserts that path directly. Large files are never read in
  full, so an 800MB archive resolves in milliseconds.
- **Drag a folder** (Chromium/Edge File System Access): the plugin walks the
  dropped directory, builds a directory fingerprint, and inserts the matched
  **absolute folder path**. Nothing is copied.
- If the original path is outside the search roots, a dialog asks for the
  absolute path manually. A secondary "Copy to workspace" action is available
  for unresolved FILES, but it is never triggered automatically.
- Click the **paperclip button** in the composer tool row to insert a manually
  typed path (absolute or relative to the session workspace).
- Supported images (PNG/JPEG/WebP/GIF) keep the normal dsh image behavior. A
  mixed drop routes images to the default composer intake and resolves only the
  rest.
- The plugin clears dsh's full-page drop overlay when it takes over a drop, so
  the "drag images here" screen never remains stuck.

## Configuration

The bundle patch inserts one host row. Override its config in the profile's
`cordis.patch.yml` or any later layer:

```yaml
- id: file-path
  config:
    importSubdir: .dsh-files          # fallback copy location
    maxFileBytes: 104857600           # decoded bytes per fallback import
    searchRoots:                      # original-path search roots
      - '~'
      # - /mnt
    searchMaxEntries: 50000           # directory entries visited per lookup
    searchSkipNames:                  # directory basenames skipped
      - .git
      - node_modules
      - .npm
      - .cache
      - .dsh
      - .Trash
    maxResolveBytes: 4294967296        # max file size for hash lookup
```

- `searchRoots` entries must be absolute or start with `~`. The session
  workspace is always searched first. Symlinks are not followed.
- `importSubdir` must be one relative segment (no `/`, `\`, `..`). Invalid
  values fail the plugin load loudly.
- Lookup caps are per call: `searchMaxEntries` bounds traversal and
  `maxResolveBytes` bounds the hashed candidate size.

## How it is built

- **Host half** (`lib/index.js`): a self-contained Cordis service registered as
  `ctx.filePathBridge`. It registers strict Typert Remote descriptors for
  `filePathBridge/resolvePath`, `filePathBridge/importFile`, and
  `filePathBridge/describe`, so wire input is schema-validated on both sides.
  `resolvePath` walks the search roots for a content-identical file and returns
  its absolute path. `importFile` is the explicit fallback: it writes the
  decoded base64 payload below `importSubdir` with a
  temporary-file-plus-rename publication path.
- **Browser half** (`lib/client.js`): a `dsh.client` module that mounts the
  three Remote methods, registers locale dictionaries, and contributes a small
  control to the `conversation.input.right` slot. Capture-phase document
  listeners take over drops and file-only pastes that contain a non-image;
  all-image batches pass through untouched, images inside a mixed batch are
  re-dispatched to the default composer listener, and a synthetic `dragend`
  resets the default drop overlay.

## Security notes

- `resolvePath` only reports the path of a file whose exact bytes the browser
  already sent as the lookup digest; it never writes and never returns a path
  that does not match the dropped content.
- The explicit fallback import only writes into the live session's own
  workspace, under the configured single-segment `importSubdir`; path
  traversal through the file name is stripped.
- The dsh Web transport's loopback/browser-trust fence still applies to
  `/api/filePathBridge/*`.
- Installing a plugin means executing its code inside the local dsh process.
  Review the source and pin versions just as you would for any plugin.

## Development

Requires Node `>=22.19`.

```sh
npm install
npm run check        # typecheck + tests + build
npm test             # host service and composer control tests
npm pack             # built tarball
```

The build script (`scripts/build.mjs`) emits the ESM host half and the
`window.__ModuleLoader__.load` CJS-factory browser half; `tsc` emits the
declaration files under `lib/types`.

## License

[MIT](./LICENSE)

Install

dsh plugin --profile web add github:dkjsiogu/dsh-file-path#7f5a75777edf3878cd27e95b44df4a50ba8a2301

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
Source