- Updated README and README.zh to clarify packaging commands and added verification step.
- Improved packaging scripts to automatically build the self-contained runtime and verify its integrity.
- Introduced new scripts for verifying harness self-containment and booting the web profile.
- Added comprehensive tests for relinking harness symlinks and ensuring self-containment.
- Updated package.json scripts to reflect new build and verification processes.
Register the dsh CLI launcher from the desktop shell, prefer the vendored
pnpm in the CLI plugin command, and update the desktop and CLI reference docs.
Co-Authored-By: Claude <noreply@anthropic.com>
Add an in-app plugin marketplace fed by a remote web catalog: the host
plugin-inventory gateway gains marketplaceList/Install/Uninstall remotes,
a durable per-user install table reconciled against the actual profile, and
git/npm/tarball/bundle install paths with non-interactive git and vendored
pnpm. The Web Settings surface gains a sibling 插件市场 tab that lists the
catalog with recommended badges, repository links, and a combined sort
(recommended first, then catalog priority, then id).
Add a host + client skill manager: list local skills by direct filesystem
discovery, and install/uninstall/toggle/edit their SKILL.md from git/npm/
tarball/local sources in the writable user root.
Co-Authored-By: Claude <noreply@anthropic.com>
The renderer opens with sandbox:true, and Electron sandboxed preload scripts
only support CommonJS. The preload was emitted as ESM (package.json is
"type": "module"), so it failed to load silently and window.dshApp was never
injected. The About section then fell back to plain-web mode: current version
stayed on the '—' fallback and check-for-updates reported up-to-date without
querying. Build the preload with esbuild into lib/types/preload.cjs and point
the main process at it.
Co-Authored-By: Claude <noreply@anthropic.com>
The About section hardcoded its version; the main process now exposes
get-app-info so it reads app.getVersion() dynamically, and the update result
carries the manifest release date. The manifest generator orders versions
numerically (0.10.0 outranks 0.9.0). A new stage-release script copies
releases.json plus installers into the deepseek-harness-web site root's
updates/, which deploy.py publishes to OSS alongside the download page, so the
app and web share one update channel. The About section also gains links to the
release site and official site.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds an unsigned, manual-download update flow. The main process fetches the OSS
updates/releases.json manifest (DSH_UPDATE_URL overrides), compares the latest
version, and on startup and hourly prompts to open the per-platform installer
URL; a preload bridge exposes the same check to the SPA's About "check for
updates" button, which renders the version, notes, and a download link.
scripts/generate-release-json.mjs builds the manifest from the packaged
.dmg/.exe. Windows nsis packaging is configured.
Co-Authored-By: Claude <noreply@anthropic.com>
Installing a plugin now activates it without a restart: the CLI boot provides a
dshReloadProfile handle that re-runs the profile composition and applies it to
the running root Include, and the install/uninstall Remotes recompose live when
the handle is present (restartRequired: false). Registry installs try the
ordered INSTALL_REGISTRIES mirrors with the official npm registry as the final
fallback, erroring only when every source is unreachable. The enable/disable
guard splits into a REQUIRED_PLUGINS blacklist and a USER_TOGGLEABLE_PLUGINS
whitelist (default toggleable) generated from the running plugin list, and the
offline optional-bundle catalog is emptied (default bundles are not
installable/uninstallable). The plugin-list tab becomes a registry install form
and shows immediate-activation instead of a restart notice.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds an install surface to the plugin-inventory gateway: availableBundles lists
the curated offline-installable optional bundles (AVAILABLE_BUNDLES); install
composes an offline bundle into the profile's dsh.profile.bundles, or for a
registry spec runs pnpm against the writable profile via the bundled Node and a
vendored pnpm (gated behind the dshAllowPluginInstall context flag, set only by
the desktop boot); uninstall removes a bundle layer. The reconcile logic from
`dsh plugin add` moves into app-boot as shared helpers. The desktop vendored
pnpm into the harness and sets the allow-install env; the plugin-list SPA gains
an installable-bundles section. Tests cover the guard, install helpers, and the
SPA section at 100% host coverage.
Co-Authored-By: Claude <noreply@anthropic.com>
Localizes the Electron application menu (文件/编辑/视图/窗口/帮助 plus the
macOS app menu), adds a branded 1024px app icon (window + per-target icns/ico),
and makes the packaged app fully self-contained. Because the harness's pnpm
workspace does not cleanly materialize via pnpm deploy or electron-builder's
dependency resolution (per-package symlinks to vendored sources, native addons,
a separate frontend dist), scripts/build-harness.mjs assembles the repository's
working runtime — node_modules, vendor, packages, native, apps/cli, apps/web,
and a bundled Node binary — into build/harness. electron-builder ships it as an
extraResource at Resources/harness, and the main process spawns that bundled
node + dsh entry when packaged (keeping the system-Node child during dev). Adds
a comprehensive README and ignores the multi-GB harness/dist from git.
Co-Authored-By: Claude <noreply@anthropic.com>
The package name must be a valid npm/pnpm workspace name; the display name
belongs in electron-builder productName. The invalid rename broke
pnpm --filter @deepseek-ai/dsh-desktop in desktop:dev.
Co-Authored-By: Claude <noreply@anthropic.com>
The web profile template mounts the bundle; apps/cli must declare it so the
harness resolves it from the installation anchor for both fresh and existing
web profiles.
Co-Authored-By: Claude <noreply@anthropic.com>
Spawn the real dsh CLI running the web profile on loopback (OS-assigned
port), parse the printed readiness URL, and open a native window at it.
The harness and its native addons stay on the system Node ABI; the main
imports only electron and node builtins.
Co-Authored-By: Claude <noreply@anthropic.com>
Every release member now declares publishConfig.access: public, so the scope no
longer mixes levels: the 221 packages/*/* and apps/* manifests join the vendored
framework and the native packages.
check-workspace-constraints drops the per-sequence expectation and holds every
release member to public, which is what stops a member from drifting back.
Access is a property of the package, not of a version: the dsh packages already
published as restricted become world-readable at their next publication.
The Codex and Claude Code subagent providers were production dependencies of
@deepseek-ai/dsh-base and mounted by its Cordis composition, so every install of
the base bundle carried two providers that only some products want.
Drop both from the base bundle's dependencies and composition. The examples keep
them as explicit dependencies, base's tests lock their absence, and the product
preset e2e mounts the providers it needs explicitly.
Cherry-picked from #2387 (two commits squashed into one).
pi-ai resolves an OAuth provider from a stored OAuth credential alone, and
this adapter builds its Models collection with no credential store and runs
no login flow. `openai-codex` — the one installed provider declaring
`auth.oauth` with no `auth.apiKey` — was therefore offered on the Models page
with the keyless placeholder every pi-ai route carries, and every request on
it failed `Provider is not configured` before going out.
`catalogProviderTakesApiKey()` answers whether pi-ai's installed provider for
a route declares the one method this adapter can supply, and the directory
skips the catalog routes that fail it. Catalog membership is unchanged, so
`declared` still answers what pi-ai ships; the profile half of the union stays
unconditional, so a route a settings document already names keeps its entry
and can be edited or deleted.
Resolution is untouched: a profile naming `apiKeyEnv` on such a route still
builds a working provider.
The shipped bundles keep ctx.sessionQuery mounted but set the new
session-query-sqlite `openAt: never` phase: searchSessions/searchEvents
fail with the typed SESSION_QUERY_SEARCH_DISABLED code before any request
normalization, node:sqlite is never imported or opened, and no source
observation or reconciliation runs. Every inherited exact read, filter,
and trace — session export descendants, subagent-fork Workspace
inheritance, title reads — keeps working, and the Web sidebar search
degrades to its designed local title/workspace matching. Enabling content
search is a one-line openAt override in a later patch layer; the web e2e
scaffold keeps it enabled as the assembled opt-in coverage.