Skip to content

Latest commit

 

History

History
109 lines (79 loc) · 4.05 KB

File metadata and controls

109 lines (79 loc) · 4.05 KB

PITFALLS.md — things this plugin learned the hard way

Read this before changing the host or client half. Every one of these was a real incident that took the app down or hung the plugin.

1. Host bundle: named exports only (no export default)

DSH's bundle loader (cordis-plugin-loader's unwrapExports) unwraps a module's default export to the bare function, silently discarding inject and name. Services the plugin declares in inject are then never injected, and at boot you get:

Error: cannot get property "webServer" without inject

…which takes the whole profile down (plugin tree failed to load). The dsh-pet host bundle avoids this by having no default export. Do the same:

export const name = "dsh-video-player";
export const inject = ["webServer"];
export function apply(ctx) { /* ... */ }

2. Client: never declare an inject you don't require

Declaring inject: ["@deepseek-ai/dsh-client-runtime"] without requiring that module in the client factory leaves the plugin forever in:

dsh-video-player: pending (waiting for service: @deepseek-ai/dsh-client-runtime)

This client is self-contained (plain DOM, native <video>/<iframe>), so it injects nothing (inject: []) and activates immediately.

3. link: installs need an import bridge

dsh plugin --profile web add <local-dir> installs the package as a link: symlink. Node resolves the host module's imports from the symlink realpath, which sits outside the profile's node_modules — so import { installSettingsSection } from "@deepseek-ai/dsh-settings" fails to resolve at boot. Bridge the imports into the plugin's own node_modules/:

mkdir -p <plugin>/node_modules/@deepseek-ai
ln -s ~/.dsh/profiles/node_modules/@deepseek-ai/dsh-settings <plugin>/node_modules/@deepseek-ai/dsh-settings
ln -s ~/.dsh/profiles/web/node_modules/schemastery <plugin>/node_modules/schemastery

(A proper npm install — not link:) doesn't need this, because the package lands inside the profile's node_modules tree.

4. A bad dsh.profile.bundles entry bricks every boot

dsh web resolves every bundle name as a real installed package with a dsh.bundle.patch, and fails loud on the first bad entry — there is no skip-fallback. A market install that writes a name without a usable bundle (e.g. @linxin666/dsh-web-ui-all, whose npm metadata has no bundle) makes the container crash-loop until package.json is fixed.

Prevention: run sh ~/.dsh/dsh-guard/bundle-validate.sh --profile web (or dsh-guard.sh check) before restarting after any plugin change; --fix removes bad entries, reset-profile.sh restores a known-good profile.

5. Test risky boots on another port

A boot failure on the live app takes the whole container down. Test first:

dsh web --port 3099 --no-open    # same profile, captured logs, no risk to :3079

(dsh web does NOT accept --profile — the subcommand already implies web.)

6. Docker-on-Windows: disable the native "open file" opener

Under Docker on a WSL2 host the kernel reports "microsoft", so DSH thinks it is in WSL and tries wslpath/powershell.exe when you click a produced file — spawn wslpath ENOENT. Set the patch-layer override:

# ~/.dsh/profiles/web/cordis.patch.yml
- id: api-gateway
  config:
    nativeOpen: false

The UI then shows produced paths as copyable text instead of a dead button.

7. Inputs inside a draggable title bar

A drag handler on the title bar swallows mousedown on child inputs/buttons, so they can't be clicked or typed in. Exclude interactive targets:

function onBarDown(e) {
  if (e.target && e.target.closest && e.target.closest("input,button,select,textarea")) return;
  /* ...start drag... */
}

Security disclaimer (for users, not just devs)

Installing a plugin runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network. Tool approvals do not sandbox plugin code. Check the source before you install, and try unfamiliar plugins somewhere that doesn't hold your keys.