diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1f405bc024..24d8f4501f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -157,20 +157,9 @@ jobs:
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: yarn-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
- - run: yarn install --immutable
- - run: yarn upstream:install
- - run: yarn workspace dsh-plugin-desktop check:win-package
- - name: Prepare WorkDSH release profile
- timeout-minutes: 15
- run: yarn workspace dsh-plugin-desktop prepare:workdsh-runtime
- - name: Prepare official Python and Node runtime
- timeout-minutes: 15
- run: yarn workspace dsh-plugin-desktop prepare:workdsh-primary-runtime
- - name: Build Windows installer
- timeout-minutes: 30
- env:
- DSH_PACKAGE_CHECK_ALREADY_RAN: '1'
- run: node dsh-plugin-desktop/scripts/package-win.ts
+ - name: Package Windows through the single release script
+ timeout-minutes: 60
+ run: node scripts/package-desktop-release.mjs --source=${{ github.ref_type == 'tag' && 'published' || 'local' }}
- name: Report unpacked Windows footprint
run: node dsh-plugin-desktop/scripts/report-package-footprint.mjs dsh-plugin-desktop/dist/win-unpacked dsh-plugin-desktop/dist/windows-footprint.json
- name: Upload Windows footprint report
@@ -220,25 +209,11 @@ jobs:
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: yarn-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('yarn.lock', '.yarnrc.yml') }}
- - run: yarn install --immutable
- - run: yarn upstream:install
- - run: yarn workspace dsh-plugin-desktop check:mac-package
- - name: Prepare WorkDSH release profile
- timeout-minutes: 15
- env:
- WORKDSH_MAC_ARCH: ${{ matrix.arch }}
- run: yarn workspace dsh-plugin-desktop prepare:workdsh-runtime
- - name: Prepare official Python and Node runtime
- timeout-minutes: 15
- env:
- WORKDSH_MAC_ARCH: ${{ matrix.arch }}
- run: yarn workspace dsh-plugin-desktop prepare:workdsh-primary-runtime
- - name: Build macOS smoke artifact
- timeout-minutes: 25
+ - name: Package macOS through the single release script
+ timeout-minutes: 55
env:
- DSH_PACKAGE_CHECK_ALREADY_RAN: '1'
WORKDSH_MAC_ARCH: ${{ matrix.arch }}
- run: node dsh-plugin-desktop/scripts/package-mac.ts
+ run: node scripts/package-desktop-release.mjs --source=${{ github.ref_type == 'tag' && 'published' || 'local' }}
- name: Upload macOS architecture DMG
uses: actions/upload-artifact@v4
with:
@@ -284,6 +259,11 @@ jobs:
node-version: 22.23.2
- name: Require one DSH version before publication
run: node scripts/verify-desktop-dsh-alignment.mjs
+ - name: Require release tag to match Desktop package version
+ env:
+ TAG: ${{ github.ref_name }}
+ run: |
+ node -e "const version = require('./dsh-plugin-desktop/package.json').version; if (process.env.TAG !== 'desktop-v' + version) { throw new Error('Release tag does not match Desktop package version: ' + process.env.TAG + ' != desktop-v' + version) }"
- name: Download desktop packages
uses: actions/download-artifact@v5
with:
@@ -308,17 +288,14 @@ jobs:
run: |
VERSION="${TAG#desktop-v}"
cat > release-notes.md <<'EOF'
- WorkDSH Desktop 提供 Windows x64 安装版和 macOS Intel x64 与 Apple Silicon arm64 独立 DMG。
+ WorkDSH Desktop 将 WorkBuddy 风格的项目、资料、专家、技能和连接器工作台与 DeepSeek Harness 插件能力结合。
- - 桌面产品名称统一为 WorkDSH
- - 安装包内置完整 WorkDSH 运行时,启动后可直接使用项目、资料库、专家、技能和连接器
- - 内置 Node 和 Python;Agent 使用 Playwright 浏览网页时,可在同一会话的右侧栏查看并操作页面
- - 应用、Dock、托盘和安装包图标统一为 WorkDSH 蓝色 W 标识
- - README 包含项目、资料库和桌面端下载说明
- - Windows 包在原生 Windows Runner 构建和验证
- - macOS 分别提供 Intel x64 和 Apple Silicon arm64 未签名预览包
+ - 技能页接入 SkillHub 目录,可查看技能图标、来源、版本与安装入口;本地技能仍可单独管理。
+ - 现有插件页可直接发现 DSH 社区插件,并打开第三方 dsh-market 目录进行搜索和安装。
+ - Desktop 使用锁定的官方 DSH 0.1.7-rc.2 Profile;内置 Node.js 和 Python,不额外打包浏览器。
+ - 提供 Windows x64 安装包,以及 macOS Intel x64、Apple Silicon arm64 未签名 DMG。
- 这是 Alpha 预发布。请在升级前备份本地配置和数据;未签名包可能触发系统安全提示。所有文件的 SHA-256 见 `SHA256SUMS`。
+ SkillHub 和 dsh-market 是独立第三方来源;目录内容并非全部预装或经 WorkDSH 审核。安装前请检查许可、依赖和版本兼容性。本次为 Alpha 预发布,升级前请备份本地配置和数据。所有文件的 SHA-256 见 `SHA256SUMS`。
EOF
gh release create "$TAG" release-assets/* \
--repo "$GITHUB_REPOSITORY" \
diff --git a/README.i18n.yaml b/README.i18n.yaml
index 64e97dd3ad..4440d9ef7e 100644
--- a/README.i18n.yaml
+++ b/README.i18n.yaml
@@ -1,5 +1,5 @@
# Bilingual-pair consistency record: the git blob hash of each side as of the last
# confirmed-consistent state. Both languages carry equal authority. Update both files
# and re-record their hashes after editing either side.
-README.md: 0d11f05e25608c5c34d30eb0b95d4e3212393f14
-README.zh-CN.md: 6d7025fa93be348b50387f5ebb1606c465e5bab4
+README.md: 636211746118f3e3d09c65a3301cd5c319a89a11
+README.zh-CN.md: 886a541b6e12b67eeb46564f3e56b1893bdf9003
diff --git a/README.md b/README.md
index 0d11f05e25..6362117461 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
WorkDSH
-A WorkBuddy-style workspace with the DSH plugin ecosystem.
-Inspired by WorkBuddy, WorkDSH brings projects, material, experts, skills, and connectors to the desktop—and extends them through DeepSeek Harness plugins.
+A WorkBuddy-style workspace where skills, experts, and plugins shape new workflows.
+WorkDSH brings material, experts, skills, and connectors into one workspace, with the SkillHub catalog and installable DSH community plugins.
Download Desktop · Explore the workflow · User guide · 简体中文
-[](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.5-alpha.21) [](https://github.com/techflag/workdsh) [](LICENSE)
+[](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.6-alpha.1) [](https://github.com/techflag/workdsh) [](LICENSE)

@@ -12,7 +12,7 @@
## A WorkBuddy-style experience, an open DSH ecosystem
-WorkDSH takes WorkBuddy's way of organizing work as its model. It recreates the core path from projects and document references to expert and skill collaboration, while retaining DeepSeek Harness models, tools, and sessions so the desktop workspace can grow through DSH plugins.
+WorkDSH draws on WorkBuddy's way of organizing projects, material, experts, skills, and connectors, and implements those pages and management features on DeepSeek Harness. DSH is a complete agent application in its own right; it composes models, tools, skill support, and UI through plugins. WorkDSH adds its workspace features through the same mechanism.
| What you need to do | Where WorkDSH helps |
| --- | --- |
@@ -40,31 +40,31 @@ This path is WorkDSH's product direction. End-to-end validation of project docum
-## WorkBuddy-style Skills meet DSH plugins
+## Skills and plugins
-This is the other half of WorkDSH: **the workflow draws on WorkBuddy; extensions use DSH**. These ecosystems serve different purposes:
+It helps to distinguish **the content people use** from **the software extension that manages it**. A skill usually consists of instructions and resources containing `SKILL.md`; it can be installed directly into the local DSH Skills directory and is not necessarily a plugin. WorkDSH's skill manager is a DSH plugin. An expert configuration is not itself a plugin either; a WorkDSH plugin manages experts. Other DSH plugins can add tools or UI features directly. The plugin system can therefore support skills, experts, and software functions without making every skill or expert a separate plugin.
-| | WorkBuddy-style Skill | DSH plugin |
-| --- | --- | --- |
-| What it adds | Reusable instructions, scripts, references, and resources | New tools, services, and workspace capabilities |
-| How it works | Import a file or ZIP containing `SKILL.md`, review it, then confirm installation | Load and compose plugins against the current DSH version |
-| In WorkDSH | Search, enable, and manage a local skill directory; migrate WorkBuddy-style and community Skills | Projects, library, experts, skills, and connectors are themselves extensions; third-party plugins can be adapted |
+DSH plugins can be individual tools or combine UI, services, and other resources into a larger application scenario. For example, a data-management plugin could add data views and processing tools, then work with skills and experts across a workflow. This illustrates what the plugin model can support; it does not mean this release bundles such a data-management system. Users can discover and install skills through SkillHub and find DSH community plugins through dsh-market. Before installing a plugin, check what it provides and whether it supports the current DSH version.
-WorkBuddy-style Skills come from a broad ecosystem. WorkDSH offers a compatible import path, **not a claim that every Skill is tested or preinstalled**. Skills with scripts, external services, or special dependencies need individual testing; third-party DSH plugins need version-specific validation too. [Skill management](workdsh-web/packages/plugins/skills/README.md) · [Plugin development](docs/plugin-development.en.md) · [Ecosystem manifesto](docs/plugin-ecosystem.en.md)
+WorkDSH connects two independently maintained catalogs: [SkillHub](https://skillhub.cn/) for Skills and [dsh-market](https://dshmarket.com/zh/) for DSH plugins. SkillHub entries show their source and version, and open the source page for license information before installation. The DSH catalog is provided by the third-party dsh-market plugin inside the existing plugin page. Catalog entries are **not all preinstalled, reviewed, or endorsed by WorkDSH**; check each item's license, dependencies, and DSH compatibility. [Skill management](workdsh-web/packages/plugins/skills/README.md) · [Plugin development](docs/plugin-development.en.md) · [Ecosystem manifesto](docs/plugin-ecosystem.en.md)
-
+
-The installable entries shown here come from the demonstration machine's local skill directory. They are neither bundled with the installer nor an officially hosted online marketplace.
+Live SkillHub results; catalog size and entries change over time. The local session shown is a demonstration environment.
+
+
+
+The third-party dsh-market plugin supplies discovery and installation. The screenshot does not imply that catalog plugins are bundled with WorkDSH.
## Download Desktop
-The current public desktop installer release is **2.0.5-alpha.21**. These links point directly to files in its [GitHub Release](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.5-alpha.21):
+The planned desktop installer release is **2.0.6-alpha.1**. Its download links will become available after the [GitHub Release](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.6-alpha.1) is published:
| Platform | Download |
| --- | --- |
-| Windows x64 | [WorkDSH Setup.exe](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-windows-x64--WorkDSH-2.0.5-x64-Setup.exe) |
-| macOS Apple Silicon | [WorkDSH arm64.dmg](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-macos-arm64--WorkDSH-2.0.5-arm64.dmg) |
-| macOS Intel | [WorkDSH x64.dmg](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-macos-x64--WorkDSH-2.0.5-x64.dmg) |
+| Windows x64 | [WorkDSH Setup.exe](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-windows-x64--WorkDSH-2.0.6-alpha.1-x64-Setup.exe) |
+| macOS Apple Silicon | [WorkDSH arm64.dmg](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-macos-arm64--WorkDSH-2.0.6-alpha.1-arm64.dmg) |
+| macOS Intel | [WorkDSH x64.dmg](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-macos-x64--WorkDSH-2.0.6-alpha.1-x64.dmg) |
Desktop installers include Node.js and Python runtimes by default, so users do not need to install them separately. This is an **Alpha release**: end-to-end project document references, expert execution, and different Office formats are still being validated. The macOS DMGs are unsigned; download updates from [Releases](https://github.com/techflag/workdsh/releases). Start with the [user guide](docs/user-guide.en.md) and [FAQ](docs/faq.en.md).
@@ -78,11 +78,11 @@ corepack yarn install --immutable
corepack yarn dev
```
-Run checks with `corepack yarn check`. [Contributing](CONTRIBUTING.en.md)
+Run checks with `corepack yarn check`. On macOS or Windows, `corepack yarn release:pack` builds the Web Profile and Desktop package from the current commit. Once the Web package is published, use `corepack yarn release:pack:published` for the final installer. Both commands share the same [packaging script](scripts/package-desktop-release.mjs). [Contributing](CONTRIBUTING.en.md)
## Community and acknowledgements
-WorkDSH builds on the open-source [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) project. Feedback and contributions: [GitHub Issues](https://github.com/techflag/workdsh/issues) · [Contributing](CONTRIBUTING.en.md)
+Thanks to the maintainers and contributors of [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness), the foundation of our runtime and plugin system; [Tencent SkillHub](https://github.com/Tencent/skillhub), whose public catalog API powers skill discovery; [@cocofhu/skillhub](https://github.com/cocofhu/skillhub), the bundled DSH SkillHub plugin; and [dsh-market](https://github.com/dsh-market/dsh-market) with the [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) catalog, which power community plugin discovery. WorkBuddy inspired the workspace design. The bundled skill-creator adaptation retains its [Apache-2.0 notice](workdsh-web/packages/plugins/skills/resources/skills/workdsh-skill-creator/NOTICE.md). Feedback and contributions: [GitHub Issues](https://github.com/techflag/workdsh/issues) · [Contributing](CONTRIBUTING.en.md)
WorkDSH uses the [MIT License](LICENSE). It is an independent community project and is not affiliated with, partnered with, authorized by, or endorsed by DeepSeek or WorkBuddy. Those names appear only to describe technical origins, compatibility, and design references. Upstream contributors shown on GitHub are inherited from synchronized commit history; this does not imply that they maintain this repository.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 6d7025fa93..886a541b6e 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,10 +1,10 @@
WorkDSH
-WorkBuddy 式工作台,连接 DSH 插件生态。
-以 WorkBuddy 为蓝本,把项目、资料、专家、技能和连接器带到桌面;用 DeepSeek Harness 插件扩展工作能力。
+WorkBuddy 式工作台,让技能、专家与插件组成更多工作场景。
+WorkDSH 将资料、专家、技能和连接器带入同一工作台;接入 SkillHub 技能目录,并支持安装 DSH 社区插件。
下载桌面版 · 了解工作流 · 使用指南 · English
-[](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.5-alpha.21) [](https://github.com/techflag/workdsh) [](LICENSE)
+[](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.6-alpha.1) [](https://github.com/techflag/workdsh) [](LICENSE)

@@ -12,7 +12,7 @@
## WorkBuddy 式体验,DSH 开放生态
-WorkDSH 以 WorkBuddy 的工作方式为蓝本,复刻从项目组织、资料引用到专家和技能协作的核心路径。它同时保留 DeepSeek Harness 的模型、工具和会话能力,让熟悉的桌面工作台可以继续通过 DSH 插件扩展。
+WorkDSH 参考 WorkBuddy 按项目组织资料、专家、技能和连接器的工作方式,并在 DeepSeek Harness 上实现这些页面和管理功能。DSH 本身也是完整的 Agent 软件;它通过插件组合模型、工具、技能支持、界面等功能,WorkDSH 则在这套机制上加入自己的工作台功能。
| 你要完成的事 | WorkDSH 提供的入口 |
| --- | --- |
@@ -40,31 +40,31 @@ WorkDSH 以 WorkBuddy 的工作方式为蓝本,复刻从项目组织、资料
-## 面向 WorkBuddy Skill,接入 DSH 插件
+## 技能与插件生态
-这是 WorkDSH 的另一半特色:**工作方式参考 WorkBuddy,能力扩展沿用 DSH**。两种生态承担不同的角色:
+这里要区分**用户使用的内容**和**实现它的软件扩展**:一个技能通常是包含 `SKILL.md` 的说明与资源,可以直接安装到本机 DSH 技能目录,它本身不一定是插件;WorkDSH 的技能管理器才是 DSH 插件。专家配置也不等于插件,管理专家的功能由 WorkDSH 插件实现。DSH 插件还可以直接增加工具或界面功能。因而插件体系能承载技能、专家及软件功能,但不能把每一个技能或专家都算成一个插件。
-| | WorkBuddy 风格 Skill | DSH 插件 |
-| --- | --- | --- |
-| 带来什么 | 可复用的工作说明、脚本、参考资料和资源 | 新的工具、服务与工作台能力 |
-| 如何使用 | 导入包含 `SKILL.md` 的文件或 ZIP,预检后确认安装 | 按当前 DSH 版本加载和组合插件 |
-| 在 WorkDSH 中 | 本地技能目录可搜索、启停和管理;可迁入 WorkBuddy 风格及社区 Skill | 项目、资料库、专家、技能、连接器本身也是扩展能力,并可适配第三方插件 |
+DSH 插件可以是单项工具,也可以组合界面、服务与其他资源,形成更完整的应用场景。例如,数据管理插件可以提供数据页面和处理工具,再与技能、专家配合完成一套流程;这是插件体系允许的扩展方向,不表示当前版本已内置这样的数据管理系统。用户可以从 SkillHub 发现和安装技能,也可以通过 dsh-market 寻找 DSH 社区插件。安装前应看插件具体提供什么,以及是否兼容当前 DSH 版本。
-WorkBuddy 风格的 Skill 来源广泛,WorkDSH 提供兼容的导入入口,**不把“可导入”说成“全部已验证或预装”**。含脚本、外部服务或特殊依赖的 Skill 要逐个测试;第三方 DSH 插件也需按当前版本验证。[技能管理](workdsh-web/packages/plugins/skills/README.md) · [插件开发](docs/plugin-development.md) · [生态倡议](docs/plugin-ecosystem.md)
+WorkDSH 接入两个独立维护的目录:[SkillHub](https://skillhub.cn/) 提供 Skill,[dsh-market](https://dshmarket.com/zh/) 提供 DSH 插件。SkillHub 条目展示来源和版本,许可证信息可到来源页核对;现有插件页通过第三方 dsh-market 插件打开社区目录。目录中的内容**并非全部预装、经 WorkDSH 审核或获得 WorkDSH 背书**,安装前应查看许可证、依赖和 DSH 版本兼容性。[技能管理](workdsh-web/packages/plugins/skills/README.md) · [插件开发](docs/plugin-development.md) · [生态倡议](docs/plugin-ecosystem.md)
-
+
-截图中的可安装条目来自演示机的本地技能目录,不代表安装包自带或官方托管的在线市场。
+SkillHub 实时目录,条目和数量会变化;截图所示本地会话为演示环境。
+
+
+
+社区插件的发现和安装由第三方 dsh-market 插件提供;截图不代表目录中的插件已随 WorkDSH 安装包提供。
## 下载桌面版
-当前公开桌面安装包为 **2.0.5-alpha.21**。以下链接直接指向 [GitHub Release](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.5-alpha.21) 中的文件:
+计划发布的桌面安装包版本为 **2.0.6-alpha.1**。发布 [GitHub Release](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.6-alpha.1) 后,以下下载链接才会生效:
| 平台 | 下载 |
| --- | --- |
-| Windows x64 | [WorkDSH Setup.exe](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-windows-x64--WorkDSH-2.0.5-x64-Setup.exe) |
-| macOS Apple Silicon | [WorkDSH arm64.dmg](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-macos-arm64--WorkDSH-2.0.5-arm64.dmg) |
-| macOS Intel | [WorkDSH x64.dmg](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-macos-x64--WorkDSH-2.0.5-x64.dmg) |
+| Windows x64 | [WorkDSH Setup.exe](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-windows-x64--WorkDSH-2.0.6-alpha.1-x64-Setup.exe) |
+| macOS Apple Silicon | [WorkDSH arm64.dmg](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-macos-arm64--WorkDSH-2.0.6-alpha.1-arm64.dmg) |
+| macOS Intel | [WorkDSH x64.dmg](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-macos-x64--WorkDSH-2.0.6-alpha.1-x64.dmg) |
Desktop 安装包默认内置 Node.js 和 Python 运行时,普通用户无需单独安装。当前为 **Alpha 版**:项目资料引用、专家执行及不同 Office 格式的端到端体验仍在验收中。macOS DMG 未签名;更新请从 [Releases](https://github.com/techflag/workdsh/releases) 下载。开始使用前请阅读[用户指南](docs/user-guide.md)和[常见问题](docs/faq.md)。
@@ -78,11 +78,11 @@ corepack yarn install --immutable
corepack yarn dev
```
-运行检查:`corepack yarn check`。[参与贡献](CONTRIBUTING.md)
+运行检查:`corepack yarn check`。在 macOS 或 Windows 上,从当前提交一键打包 Web Profile 与 Desktop:`corepack yarn release:pack`;Web 包发布后,正式安装包使用 `corepack yarn release:pack:published`。两条命令使用同一个[打包脚本](scripts/package-desktop-release.mjs)。[参与贡献](CONTRIBUTING.md)
## 社区与致谢
-WorkDSH 基于开源项目 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 构建。反馈与参与:[GitHub Issues](https://github.com/techflag/workdsh/issues) · [参与贡献](CONTRIBUTING.md)
+感谢 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 社区提供运行时与插件底座;[腾讯 SkillHub](https://github.com/Tencent/skillhub) 提供公开的技能目录 API;[@cocofhu/skillhub](https://github.com/cocofhu/skillhub) 提供随桌面版集成的 DSH SkillHub 插件;[dsh-market](https://github.com/dsh-market/dsh-market) 与 [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) 社区提供插件市场与目录。WorkBuddy 是工作台设计参考。内置 skill-creator 的改编保留了原 [Apache-2.0 来源说明](workdsh-web/packages/plugins/skills/resources/skills/workdsh-skill-creator/NOTICE.md)。反馈与参与:[GitHub Issues](https://github.com/techflag/workdsh/issues) · [参与贡献](CONTRIBUTING.md)
WorkDSH 采用 [MIT License](LICENSE),是独立社区项目,与 DeepSeek 或 WorkBuddy 不存在隶属、合作、授权或背书关系。相关名称仅用于说明技术来源、兼容性与设计参考。GitHub Contributors 中的上游贡献者来自继承和同步的提交历史,不表示其参与本仓库维护。
diff --git a/dsh-plugin-desktop/THIRD_PARTY_NOTICES.md b/dsh-plugin-desktop/THIRD_PARTY_NOTICES.md
index 8c7d8e0d46..22878489c3 100644
--- a/dsh-plugin-desktop/THIRD_PARTY_NOTICES.md
+++ b/dsh-plugin-desktop/THIRD_PARTY_NOTICES.md
@@ -11,5 +11,19 @@ third-party notices are maintained by the upstream project:
WorkDSH bundles and their dependencies retain their own license terms, which
must be checked from the exact release artifacts used for an installer.
+The Desktop Profile bundles two separate third-party plugins:
+
+- [`@cocofhu/skillhub`](https://www.npmjs.com/package/@cocofhu/skillhub)
+ version 0.2.16 for SkillHub integration. Source and MIT license:
+ .
+- [`dshmarket`](https://www.npmjs.com/package/dshmarket) version 1.66.1 for
+ DSH community plugin discovery. Source and MIT license:
+ .
+
+The SkillHub catalog and API are maintained separately by
+[`Tencent/skillhub`](https://github.com/Tencent/skillhub). The plugin catalog
+used by dshmarket is maintained by
+[`awesome-dsh-plugin`](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin).
+
This file intentionally does not freeze a dependency inventory from an older
DSH release. Check the bundled Profile and its license files when publishing.
diff --git a/dsh-plugin-desktop/package.json b/dsh-plugin-desktop/package.json
index 279e9d2cea..aa0b403272 100644
--- a/dsh-plugin-desktop/package.json
+++ b/dsh-plugin-desktop/package.json
@@ -1,6 +1,6 @@
{
"name": "dsh-plugin-desktop",
- "version": "2.0.5",
+ "version": "2.0.6-alpha.1",
"description": "WorkDSH Electron carrier for a pinned DeepSeek Harness runtime Profile",
"license": "MIT",
"publishConfig": {
diff --git a/dsh-plugin-desktop/scripts/prepare-workdsh-runtime.mjs b/dsh-plugin-desktop/scripts/prepare-workdsh-runtime.mjs
index 07fcb9f381..af4e023663 100644
--- a/dsh-plugin-desktop/scripts/prepare-workdsh-runtime.mjs
+++ b/dsh-plugin-desktop/scripts/prepare-workdsh-runtime.mjs
@@ -8,14 +8,18 @@ import { DSH_VERSION } from './runtime-version.mjs'
import { PRODUCT_PACKAGES, RELEASE_PACKAGES } from './workdsh-package-boundary.mjs'
import { verifyPackageDshReferences, verifyProfileRelease } from './verify-profile-release.mjs'
-const WORKDSH_VERSION = '0.1.0-alpha.13'
+const WORKDSH_VERSION = '0.1.0-alpha.14'
const desktopRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const output = join(desktopRoot, 'build', 'workdsh-runtime')
const destination = join(output, 'profiles', 'workdsh')
const packageCache = join(output, 'package-cache')
const cli = join(destination, 'node_modules', '@deepseek-ai', 'dsh', 'lib', 'bin.js')
const releaseMarker = '.workdsh-desktop-release.json'
-const profileLayout = 'five-product-plugins-v1'
+const profileLayout = 'five-product-plugins-skillhub-dshmarket-v1'
+const SKILLHUB_PACKAGE = '@cocofhu/skillhub'
+const SKILLHUB_VERSION = '0.2.16'
+const MARKET_PACKAGE = 'dshmarket'
+const MARKET_VERSION = '1.66.1'
const productPackages = new Set(PRODUCT_PACKAGES)
function run(command, args, options = {}) {
@@ -55,11 +59,19 @@ async function installReleasedProfile(output) {
const releaseDir = join(desktopRoot, 'build', `.workdsh-release-${WORKDSH_VERSION}`)
mkdirSync(releaseDir, { recursive: true })
const base = `https://github.com/techflag/workdsh/releases/download/v${WORKDSH_VERSION}`
+ const localReleaseDir = process.env.WORKDSH_USE_LOCAL_RELEASE === '1'
+ ? resolve(desktopRoot, '..', 'workdsh-web', '.artifacts', `project-v${WORKDSH_VERSION}`)
+ : undefined
+ if (localReleaseDir && !existsSync(join(localReleaseDir, 'release-manifest.json'))) {
+ throw new Error(`Local WorkDSH release candidate is missing: ${localReleaseDir}`)
+ }
const manifestPath = join(releaseDir, 'release-manifest.json')
- await download(`${base}/release-manifest.json`, manifestPath)
+ if (localReleaseDir) cpSync(join(localReleaseDir, 'release-manifest.json'), manifestPath)
+ else await download(`${base}/release-manifest.json`, manifestPath)
const rawInstallerPath = join(releaseDir, 'install-workdsh.original.mjs')
const installerPath = join(releaseDir, 'install-workdsh.mjs')
- await download(`${base}/install-workdsh.mjs`, rawInstallerPath)
+ if (localReleaseDir) cpSync(join(localReleaseDir, 'install-workdsh.mjs'), rawInstallerPath)
+ else await download(`${base}/install-workdsh.mjs`, rawInstallerPath)
let installer = readFileSync(rawInstallerPath, 'utf8')
installer = replaceRequired(installer,
'profilePackage.packageManager = manifest.packageManager;',
@@ -125,7 +137,8 @@ async function installReleasedProfile(output) {
}
writeFileSync(installerPath, installer)
for (const item of manifest.packages) {
- await download(`${base}/${item.filename}`, join(releaseDir, item.filename))
+ if (localReleaseDir) cpSync(join(localReleaseDir, item.filename), join(releaseDir, item.filename))
+ else await download(`${base}/${item.filename}`, join(releaseDir, item.filename))
}
mkdirSync(output, { recursive: true })
@@ -183,6 +196,16 @@ async function installReleasedProfile(output) {
// layer, while only the five product bundles are directly manageable.
run(process.execPath, [pnpmCli, '--dir', destination, 'install', '--lockfile-only', '--offline'])
run(process.execPath, [pnpmCli, '--dir', destination, 'install', '--frozen-lockfile', '--offline'])
+ // Keep the third-party SkillHub integration in the same DSH Profile. It
+ // supplies SkillHub search and a DSH plugin catalogue without a second host.
+ run(process.execPath, [pnpmCli, '--dir', destination, 'add', '--save-exact', `${SKILLHUB_PACKAGE}@${SKILLHUB_VERSION}`, `${MARKET_PACKAGE}@${MARKET_VERSION}`])
+ const installedSkillHub = JSON.parse(readFileSync(join(destination, 'node_modules', SKILLHUB_PACKAGE, 'package.json'), 'utf8'))
+ if (installedSkillHub.version !== SKILLHUB_VERSION) throw new Error('Pinned SkillHub plugin version is missing')
+ const installedMarket = JSON.parse(readFileSync(join(destination, 'node_modules', MARKET_PACKAGE, 'package.json'), 'utf8'))
+ if (installedMarket.version !== MARKET_VERSION) throw new Error('Pinned dshmarket plugin version is missing')
+ const profileWithSkillHub = JSON.parse(readFileSync(profilePath, 'utf8'))
+ profileWithSkillHub.dsh.profile.bundles = [...new Set([...profileWithSkillHub.dsh.profile.bundles, SKILLHUB_PACKAGE, MARKET_PACKAGE])]
+ writeFileSync(profilePath, JSON.stringify(profileWithSkillHub, null, 2) + '\n')
const config = spawnSync(process.execPath, [cli, '--profile', 'workdsh', '--dump-config'], {
encoding: 'utf8', maxBuffer: 8 * 1024 * 1024, env: { ...process.env, DSH_HOME: output },
})
@@ -191,6 +214,8 @@ async function installReleasedProfile(output) {
for (const id of ['workdsh-installation-probe', 'workdsh-identity-local', 'workdsh-access', 'workdsh-audit', 'workdsh-office']) {
if (!config.stdout.includes(`id: ${id}`)) throw new Error(`Internal WorkDSH service is missing: ${id}`)
}
+ if (!config.stdout.includes('id: skillhub')) throw new Error('SkillHub plugin is missing from the WorkDSH Profile')
+ if (!config.stdout.includes('id: dsh-market')) throw new Error('dshmarket plugin is missing from the WorkDSH Profile')
run(process.execPath, [join(desktopRoot, 'scripts', 'verify-product-plugin-inventory.mjs'), output], {
env: { ...process.env, DSH_HOME: output },
})
@@ -219,6 +244,12 @@ const internalPatch = profile => {
const isPreparedProfile = candidate => {
if (installedDshVersion(candidate) !== DSH_VERSION) return false
if (!releasePackages.every(name => existsSync(join(candidate, 'node_modules', name, 'package.json')))) return false
+ try {
+ const skillHub = JSON.parse(readFileSync(join(candidate, 'node_modules', SKILLHUB_PACKAGE, 'package.json'), 'utf8'))
+ if (skillHub.version !== SKILLHUB_VERSION) return false
+ const market = JSON.parse(readFileSync(join(candidate, 'node_modules', MARKET_PACKAGE, 'package.json'), 'utf8'))
+ if (market.version !== MARKET_VERSION) return false
+ } catch { return false }
try {
const marker = JSON.parse(readFileSync(join(candidate, releaseMarker), 'utf8'))
if (marker.release !== WORKDSH_VERSION || marker.harness !== DSH_VERSION || marker.layout !== profileLayout) return false
@@ -240,6 +271,10 @@ const isPreparedProfile = candidate => {
const manifest = JSON.parse(readFileSync(resolve(candidate, '..', '..', 'package-cache', 'release-manifest.json'), 'utf8'))
const selected = profile.dsh?.profile?.bundles ?? []
return [...productPackages].every(name => selected.includes(name)) &&
+ selected.includes(SKILLHUB_PACKAGE) &&
+ selected.includes(MARKET_PACKAGE) &&
+ profile.dependencies?.[SKILLHUB_PACKAGE] === SKILLHUB_VERSION &&
+ profile.dependencies?.[MARKET_PACKAGE] === MARKET_VERSION &&
supportPackages.every(name => !selected.includes(name)) &&
supportPackages.every(name => !Object.hasOwn(profile.dependencies ?? {}, name)) &&
readFileSync(join(candidate, 'cordis.patch.yml'), 'utf8').startsWith(internalPatch(candidate)) &&
diff --git a/dsh-plugin-desktop/scripts/verify-product-plugin-inventory.mjs b/dsh-plugin-desktop/scripts/verify-product-plugin-inventory.mjs
index b78577b0f9..cedcba4207 100644
--- a/dsh-plugin-desktop/scripts/verify-product-plugin-inventory.mjs
+++ b/dsh-plugin-desktop/scripts/verify-product-plugin-inventory.mjs
@@ -38,6 +38,14 @@ try {
throw new Error(`WorkDSH product bundle is inactive or invalid: ${bundle.name}: ${JSON.stringify(bundle)}`)
}
}
+ const skillHub = (await ctx.pluginManager.listBundles()).find(row => row.name === '@cocofhu/skillhub')
+ if (!skillHub?.enabled || !skillHub.installed || skillHub.error) {
+ throw new Error(`SkillHub DSH plugin is inactive or invalid: ${JSON.stringify(skillHub)}`)
+ }
+ const market = (await ctx.pluginManager.listBundles()).find(row => row.name === 'dshmarket')
+ if (!market?.enabled || !market.installed || market.error) {
+ throw new Error(`dshmarket DSH plugin is inactive or invalid: ${JSON.stringify(market)}`)
+ }
console.log(`Verified plugin manager exposes exactly five WorkDSH product bundles: ${names.join(', ')}`)
} finally {
try {
diff --git a/dsh-plugin-desktop/src/workdsh-main.ts b/dsh-plugin-desktop/src/workdsh-main.ts
index a5f8fa7766..bd110ff368 100644
--- a/dsh-plugin-desktop/src/workdsh-main.ts
+++ b/dsh-plugin-desktop/src/workdsh-main.ts
@@ -169,6 +169,7 @@ function startRuntime(home: string, profileDir: string): void {
env: {
...process.env,
DSH_HOME: home,
+ DSH_AGENTS_HOME: join(home, 'agents'),
DSH_BUNDLED_PRIMARY_RUNTIME: bundledPrimaryRuntime(),
DSH_ELECTRON_EXECUTABLE: process.execPath,
ELECTRON_RUN_AS_NODE: undefined,
diff --git a/package.json b/package.json
index a7a5424017..1b3f13c892 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,8 @@
"dist:mac-smoke": "yarn workspace dsh-plugin-desktop dist:mac-smoke",
"dist:win": "yarn workspace dsh-plugin-desktop dist:win",
"dist:win-portable": "yarn workspace dsh-plugin-desktop dist:win-portable",
+ "release:pack": "node scripts/package-desktop-release.mjs --source=local",
+ "release:pack:published": "node scripts/package-desktop-release.mjs --source=published",
"upstream:version": "cd deepseek-harness && corepack pnpm --version",
"upstream:install": "cd deepseek-harness && CI=true corepack pnpm install --frozen-lockfile",
"upstream:build": "cd deepseek-harness && corepack pnpm run build",
diff --git a/scripts/package-desktop-release.mjs b/scripts/package-desktop-release.mjs
new file mode 100644
index 0000000000..a5dc207ab7
--- /dev/null
+++ b/scripts/package-desktop-release.mjs
@@ -0,0 +1,58 @@
+#!/usr/bin/env node
+
+import { execFileSync, spawnSync } from 'node:child_process'
+import { readFileSync } from 'node:fs'
+import { dirname, join, resolve } from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
+const webRoot = join(root, 'workdsh-web')
+const args = process.argv.slice(2)
+const sourceArg = args.find(arg => arg.startsWith('--source='))
+if (args.length !== 1 || !sourceArg || !['local', 'published'].includes(sourceArg.slice('--source='.length))) {
+ throw new Error('Usage: node scripts/package-desktop-release.mjs --source=local|published')
+}
+const source = sourceArg.slice('--source='.length)
+const platform = process.platform
+if (platform !== 'darwin' && platform !== 'win32') {
+ throw new Error(`Desktop installer packaging requires macOS or Windows; found ${platform}`)
+}
+const corepack = platform === 'win32' ? 'corepack.cmd' : 'corepack'
+
+function run(label, command, commandArgs, { cwd = root, env = process.env } = {}) {
+ console.log(`\n==> ${label}`)
+ const result = spawnSync(command, commandArgs, {
+ cwd,
+ env,
+ stdio: 'inherit',
+ shell: platform === 'win32' && command === corepack,
+ })
+ if (result.error) throw result.error
+ if (result.status !== 0) throw new Error(`${label} failed with exit code ${result.status}`)
+}
+
+run('Install Desktop dependencies', corepack, ['yarn', 'install', '--immutable'])
+run('Install the pinned official DSH checkout dependencies', corepack, ['yarn', 'upstream:install'])
+run('Check Desktop packaging', corepack, ['yarn', 'workspace', 'dsh-plugin-desktop', platform === 'win32' ? 'check:win-package' : 'check:mac-package'])
+
+if (source === 'local') {
+ run('Install Web dependencies', corepack, ['pnpm', 'install', '--frozen-lockfile'], { cwd: webRoot })
+ run('Build WorkDSH Web and plugins', corepack, ['pnpm', 'build'], { cwd: webRoot })
+ run('Pack this commit\'s WorkDSH Profile', corepack, ['pnpm', 'release:project:pack'], { cwd: webRoot })
+ const version = JSON.parse(readFileSync(join(webRoot, 'package.json'), 'utf8')).version
+ const manifest = JSON.parse(readFileSync(join(webRoot, '.artifacts', `project-v${version}`, 'release-manifest.json'), 'utf8'))
+ const commit = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim()
+ if (manifest.version !== version || manifest.sourceCommit !== commit || manifest.sourceDirty) {
+ const changed = execFileSync('git', ['diff', '--name-only', '--ignore-submodules=dirty', 'HEAD', '--', 'workdsh-web', 'upstream.json', 'deepseek-harness'], { cwd: root, encoding: 'utf8' }).trim()
+ throw new Error(`The local Web release candidate is not a clean package of this commit: version=${manifest.version}/${version}, commit=${manifest.sourceCommit}/${commit}, sourceDirty=${manifest.sourceDirty}, changed=${changed || '(none)'}`)
+ }
+}
+
+const runtimeEnv = { ...process.env, WORKDSH_USE_LOCAL_RELEASE: source === 'local' ? '1' : '0' }
+run('Prepare the single WorkDSH DSH Profile', corepack, ['yarn', 'workspace', 'dsh-plugin-desktop', 'prepare:workdsh-runtime'], { env: runtimeEnv })
+run('Prepare bundled Python and Node.js', corepack, ['yarn', 'workspace', 'dsh-plugin-desktop', 'prepare:workdsh-primary-runtime'], { env: runtimeEnv })
+run('Build the Desktop installer', process.execPath, [join(root, 'dsh-plugin-desktop', 'scripts', platform === 'win32' ? 'package-win.ts' : 'package-mac.ts')], {
+ env: { ...runtimeEnv, DSH_PACKAGE_CHECK_ALREADY_RAN: '1' },
+})
+
+console.log(`\nDesktop package completed using ${source === 'local' ? 'this commit\'s Web Profile' : 'the published Web Profile'}.`)
diff --git a/workdsh-web/README.md b/workdsh-web/README.md
index 69655cd50c..5a792bc3c8 100644
--- a/workdsh-web/README.md
+++ b/workdsh-web/README.md
@@ -5,19 +5,19 @@
WorkDSH is an open-source AI workspace built on the official DeepSeek Harness. Organize conversations, material and capabilities in projects, reuse your local Library, and review editable deliverables alongside the task.
-**Desktop v2.0.5-alpha.21 · Web/plugins v0.1.0-alpha.13 · Alpha preview**
+**Desktop v2.0.6-alpha.1 · Web/plugins v0.1.0-alpha.14 · Alpha preview**
-[Desktop downloads](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.5-alpha.21) · [Web/plugin download](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.13) · [Release notes](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.13) · [Quick start](#quick-start) · [Website](https://techflag.github.io/workdsh/) · [Gitee mirror](https://gitee.com/techflag/workdsh)
+[Desktop downloads](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.6-alpha.1) · [Web/plugin download](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.14) · [Release notes](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.14) · [Quick start](#quick-start) · [Website](https://techflag.github.io/workdsh/) · [Gitee mirror](https://gitee.com/techflag/workdsh)
## Download WorkDSH Desktop
| System | Installer |
| --- | --- |
-| Windows x64 | [Download Setup.exe](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-windows-x64--WorkDSH-2.0.5-x64-Setup.exe) |
-| macOS Apple Silicon | [Download arm64 DMG](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-macos-arm64--WorkDSH-2.0.5-arm64.dmg) |
-| macOS Intel | [Download x64 DMG](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-macos-x64--WorkDSH-2.0.5-x64.dmg) |
+| Windows x64 | [Download Setup.exe](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-windows-x64--WorkDSH-2.0.6-alpha.1-x64-Setup.exe) |
+| macOS Apple Silicon | [Download arm64 DMG](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-macos-arm64--WorkDSH-2.0.6-alpha.1-arm64.dmg) |
+| macOS Intel | [Download x64 DMG](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-macos-x64--WorkDSH-2.0.6-alpha.1-x64.dmg) |
-These Alpha installers bundle WorkDSH v0.1.0-alpha.13 and the official Harness 0.1.7-rc.2 Profile, including Node and Python. The task browser reuses Electron; no second browser binary is bundled. macOS DMGs are unsigned previews; checksums are in the [Desktop release](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.5-alpha.21).
+These Alpha installers bundle WorkDSH v0.1.0-alpha.14 and the official Harness 0.1.7-rc.2 Profile, including Node and Python. The task browser reuses Electron; no second browser binary is bundled. macOS DMGs are unsigned previews; checksums are in the [Desktop release](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.6-alpha.1).

@@ -32,7 +32,7 @@ These Alpha installers bundle WorkDSH v0.1.0-alpha.13 and the official Harness 0
| Office deliverables | Preview and edit supported document, presentation, spreadsheet, HTML and PDF working copies. Format fidelity varies. |
| Activity | Inspect native task and child-agent activity; an expert-team label alone does not mean multiple agents are executing. |
-The alpha.13 bundle includes **12 installable modules**, including Projects, Library and the managed browser session provider, built against the published Harness 0.1.7-rc.2 APIs. Native attachment, input and send behavior remain owned by Harness. In Desktop, Agent browser work appears in the right sidebar on the same Electron page used by its tools.
+The alpha.14 bundle includes **12 installable modules**, including Projects, Library and the managed browser session provider, built against the published Harness 0.1.7-rc.2 APIs. Native attachment, input and send behavior remain owned by Harness. In Desktop, Agent browser work appears in the right sidebar on the same Electron page used by its tools.
## Screenshots
@@ -45,13 +45,6 @@ Full application captures from a local workspace; example projects, installed sk
-
-Skills — local installed catalog
-
-
-
-
-
Office example — conversation and HTML deliverable
@@ -75,7 +68,7 @@ Earlier local preview showing the artifact workflow; it is not an alpha.8 accept
Requirements: Node.js `^22.19.0 || >=24.0.0`, Corepack/pnpm and the official `dsh` CLI **0.1.7-rc.2**.
-1. Download all 12 `.tgz` packages, `release-manifest.json`, `SHA256SUMS` and `install-workdsh.mjs` from the [alpha.13 release](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.13) into one directory.
+1. Download all 12 `.tgz` packages, `release-manifest.json`, `SHA256SUMS` and `install-workdsh.mjs` from the [alpha.14 release](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.14) into one directory.
2. For an upgrade, stop the target Profile and keep a recoverable backup of its configuration and data.
3. Run from the download directory:
diff --git a/workdsh-web/README.zh-CN.md b/workdsh-web/README.zh-CN.md
index d313b86dec..a5072162e2 100644
--- a/workdsh-web/README.zh-CN.md
+++ b/workdsh-web/README.zh-CN.md
@@ -5,19 +5,19 @@
WorkDSH 是基于官方 DeepSeek Harness 的开源 AI 工作台。把对话、资料与能力组织到项目里,复用本地资料库,在任务旁查看和编辑交付成果。
-**桌面版 v2.0.5-alpha.21 · Web/插件 v0.1.0-alpha.13 · Alpha 预览版**
+**桌面版 v2.0.6-alpha.1 · Web/插件 v0.1.0-alpha.14 · Alpha 预览版**
-[桌面版下载](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.5-alpha.21) · [Web/插件下载](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.13) · [更新说明](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.13) · [快速开始](#快速开始) · [官网](https://techflag.github.io/workdsh/) · [Gitee 镜像](https://gitee.com/techflag/workdsh)
+[桌面版下载](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.6-alpha.1) · [Web/插件下载](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.14) · [更新说明](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.14) · [快速开始](#快速开始) · [官网](https://techflag.github.io/workdsh/) · [Gitee 镜像](https://gitee.com/techflag/workdsh)
## 下载 WorkDSH 桌面版
| 系统 | 安装包 |
| --- | --- |
-| Windows x64 | [下载 Setup.exe](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-windows-x64--WorkDSH-2.0.5-x64-Setup.exe) |
-| macOS Apple 芯片 | [下载 arm64 DMG](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-macos-arm64--WorkDSH-2.0.5-arm64.dmg) |
-| macOS Intel | [下载 x64 DMG](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.5-alpha.21/dsh-plugin-desktop-macos-x64--WorkDSH-2.0.5-x64.dmg) |
+| Windows x64 | [下载 Setup.exe](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-windows-x64--WorkDSH-2.0.6-alpha.1-x64-Setup.exe) |
+| macOS Apple 芯片 | [下载 arm64 DMG](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-macos-arm64--WorkDSH-2.0.6-alpha.1-arm64.dmg) |
+| macOS Intel | [下载 x64 DMG](https://github.com/techflag/workdsh/releases/download/desktop-v2.0.6-alpha.1/dsh-plugin-desktop-macos-x64--WorkDSH-2.0.6-alpha.1-x64.dmg) |
-这批 Alpha 安装包内置 WorkDSH v0.1.0-alpha.13 和官方 Harness 0.1.7-rc.2 Profile,包含 Node 与 Python。任务浏览器复用 Electron,不额外打包浏览器。macOS DMG 是未签名预览包;校验文件见[桌面版 Release](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.5-alpha.21)。
+这批 Alpha 安装包内置 WorkDSH v0.1.0-alpha.14 和官方 Harness 0.1.7-rc.2 Profile,包含 Node 与 Python。任务浏览器复用 Electron,不额外打包浏览器。macOS DMG 是未签名预览包;校验文件见[桌面版 Release](https://github.com/techflag/workdsh/releases/tag/desktop-v2.0.6-alpha.1)。

@@ -32,7 +32,7 @@ WorkDSH 是基于官方 DeepSeek Harness 的开源 AI 工作台。把对话、
| Office 成果 | 预览和编辑支持范围内的文档、演示文稿、表格、HTML 与 PDF 工作副本;不同格式的保真范围有差异。 |
| 活动记录 | 查看原生任务与子代理活动;显示专家团名称不代表多个成员已经执行。 |
-alpha.13 整包包含 **12 个可安装模块**,包括项目、资料库和受管浏览器会话,并适配 Harness 0.1.7-rc.2 的公开接口。Desktop 中 Agent 操作的网页显示在右侧任务浏览器,与工具操作同一 Electron 页面。附件、输入和发送继续使用 Harness 原生能力。
+alpha.14 整包包含 **12 个可安装模块**,包括项目、资料库和受管浏览器会话,并适配 Harness 0.1.7-rc.2 的公开接口。Desktop 中 Agent 操作的网页显示在右侧任务浏览器,与工具操作同一 Electron 页面。附件、输入和发送继续使用 Harness 原生能力。
## 页面截图
@@ -45,13 +45,6 @@ alpha.13 整包包含 **12 个可安装模块**,包括项目、资料库和受
-
-技能:本地已安装目录
-
-
-
-
-
Office 示例:对话与 HTML 成果
@@ -75,7 +68,7 @@ alpha.13 整包包含 **12 个可安装模块**,包括项目、资料库和受
环境要求:Node.js `^22.19.0 || >=24.0.0`、Corepack/pnpm,以及官方 **0.1.7-rc.2** 版本的 `dsh` CLI。
-1. 从 [alpha.13 Release](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.13) 下载全部 12 个 `.tgz`、`release-manifest.json`、`SHA256SUMS` 和 `install-workdsh.mjs`,放入同一目录。
+1. 从 [alpha.14 Release](https://github.com/techflag/workdsh/releases/tag/v0.1.0-alpha.14) 下载全部 12 个 `.tgz`、`release-manifest.json`、`SHA256SUMS` 和 `install-workdsh.mjs`,放入同一目录。
2. 升级已有 Profile 时,先停止运行,并保留配置和数据的可恢复备份。
3. 在下载目录执行:
diff --git a/workdsh-web/RELEASE-NOTES.md b/workdsh-web/RELEASE-NOTES.md
new file mode 100644
index 0000000000..b8118774bb
--- /dev/null
+++ b/workdsh-web/RELEASE-NOTES.md
@@ -0,0 +1,7 @@
+# WorkDSH Web and plugins v0.1.0-alpha.14
+
+This release packages the current WorkDSH Profile for DeepSeek Harness 0.1.7-rc.2. It adds a SkillHub catalog to the skill page, with skill icons, search, pagination, source links, version display and managed installation. The existing DSH plugin page can open the separately maintained dsh-market catalog without modifying official Harness source.
+
+The release contains 12 WorkDSH packages, the install script, a manifest and SHA-256 checksums. SkillHub and dsh-market are third-party services or plugins; individual catalog entries are not bundled or approved by WorkDSH. Check each item's license, dependencies and DSH compatibility before installation.
+
+This is an Alpha release. Back up local profiles before upgrading. The desktop installer is published separately from this Web and plugin archive.
diff --git a/workdsh-web/assets/screenshots/workdsh-dshmarket-2026-09.png b/workdsh-web/assets/screenshots/workdsh-dshmarket-2026-09.png
new file mode 100644
index 0000000000..7c4dfae49b
Binary files /dev/null and b/workdsh-web/assets/screenshots/workdsh-dshmarket-2026-09.png differ
diff --git a/workdsh-web/assets/screenshots/workdsh-skillhub-2026-09.png b/workdsh-web/assets/screenshots/workdsh-skillhub-2026-09.png
new file mode 100644
index 0000000000..b8e095d0e9
Binary files /dev/null and b/workdsh-web/assets/screenshots/workdsh-skillhub-2026-09.png differ
diff --git a/workdsh-web/assets/screenshots/workdsh-skills-alpha8-dark.png b/workdsh-web/assets/screenshots/workdsh-skills-alpha8-dark.png
deleted file mode 100644
index 33ab06393a..0000000000
Binary files a/workdsh-web/assets/screenshots/workdsh-skills-alpha8-dark.png and /dev/null differ
diff --git a/workdsh-web/package.json b/workdsh-web/package.json
index ac3712e51a..ee5ede2530 100644
--- a/workdsh-web/package.json
+++ b/workdsh-web/package.json
@@ -1,6 +1,6 @@
{
"name": "workdsh",
- "version": "0.1.0-alpha.13",
+ "version": "0.1.0-alpha.14",
"private": true,
"type": "module",
"description": "WorkDSH planning and plugin workspace",
diff --git a/workdsh-web/packages/bundle/locale/en.json b/workdsh-web/packages/bundle/locale/en.json
new file mode 100644
index 0000000000..7c728b1c45
--- /dev/null
+++ b/workdsh-web/packages/bundle/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Composes the WorkDSH workspace, capabilities and default DSH plugins."
+ }
+}
diff --git a/workdsh-web/packages/bundle/locale/zh.json b/workdsh-web/packages/bundle/locale/zh.json
new file mode 100644
index 0000000000..8bbaa3de94
--- /dev/null
+++ b/workdsh-web/packages/bundle/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "组合工作区、能力中心和默认 DSH 插件。"
+ }
+}
diff --git a/workdsh-web/packages/bundle/package.json b/workdsh-web/packages/bundle/package.json
index ddf40f6413..e546f2a1f9 100644
--- a/workdsh-web/packages/bundle/package.json
+++ b/workdsh-web/packages/bundle/package.json
@@ -1,9 +1,9 @@
{
"name": "workdsh-bundle",
- "version": "0.1.0-alpha.52",
+ "version": "0.1.0-alpha.53",
"private": true,
"type": "module",
- "description": "WorkDSH default Harness bundle and client composition",
+ "description": "Composes the WorkDSH workspace, capabilities and default DSH plugins.",
"exports": {
"./probe": {
"types": "./dist/probe.d.ts",
@@ -16,13 +16,16 @@
".": {
"types": "./dist/probe.d.ts",
"default": "./dist/probe.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"dist",
"cordis.patch.yml",
"README.md",
- "CHANGELOG.md"
+ "CHANGELOG.md",
+ "locale/*.json"
],
"dsh": {
"bundle": {
@@ -58,6 +61,7 @@
"@deepseek-ai/dsh-client-connection": "0.1.7-rc.2",
"@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.2",
"@deepseek-ai/dsh-client-ui-layout": "0.1.7-rc.2",
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "0.1.7-rc.2",
"@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.2",
"@deepseek-ai/dsh-client-ui-sidebar": "0.1.7-rc.2",
"@deepseek-ai/dsh-client-ui-sidebar-right": "0.1.7-rc.2",
diff --git a/workdsh-web/packages/bundle/src/client/components/CommunityMarket.tsx b/workdsh-web/packages/bundle/src/client/components/CommunityMarket.tsx
new file mode 100644
index 0000000000..4e41855cfd
--- /dev/null
+++ b/workdsh-web/packages/bundle/src/client/components/CommunityMarket.tsx
@@ -0,0 +1,61 @@
+import { useEffect, useRef, type ReactNode } from 'react';
+
+const discoverySeenKey = 'workdsh.community-market.discovery-seen';
+const discoveryCardStyle = `
+[data-plugin-item="workdsh-community-market"] {
+ background: linear-gradient(90deg,
+ color-mix(in srgb, var(--dsw-alias-state-business-primary) 17%, transparent),
+ color-mix(in srgb, var(--dsw-alias-state-business-primary) 6%, transparent));
+ box-shadow: inset 3px 0 0 var(--dsw-alias-state-business-primary);
+ outline: 1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 28%, transparent);
+}
+[data-plugin-item="workdsh-community-market"]:hover {
+ background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 21%, transparent);
+}
+`;
+
+function DiscoverySummary(): ReactNode {
+ const badge = useRef(null);
+ useEffect(() => {
+ const style = document.createElement('style');
+ style.textContent = discoveryCardStyle;
+ document.head.append(style);
+ return () => style.remove();
+ }, []);
+ useEffect(() => {
+ const node = badge.current;
+ if (!node || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
+ try { if (window.localStorage.getItem(discoverySeenKey)) return; }
+ catch { return; }
+ const observer = new IntersectionObserver(entries => {
+ if (!entries.some(entry => entry.isIntersecting)) return;
+ observer.disconnect();
+ node.animate([
+ { transform: 'scale(1)', boxShadow: '0 0 0 0 var(--dsw-alias-state-business-primary)' },
+ { transform: 'scale(1.08)', boxShadow: '0 0 0 7px transparent', offset: 0.5 },
+ { transform: 'scale(1)', boxShadow: '0 0 0 0 transparent' },
+ ], { duration: 1000, easing: 'ease-out' });
+ try { window.localStorage.setItem(discoverySeenKey, '1'); } catch { /* Storage is optional. */ }
+ });
+ observer.observe(node);
+ return () => observer.disconnect();
+ }, []);
+ return
+ 第三方市场 →
+ 搜索并安装 DSH 社区插件
+ ;
+}
+
+/** dshmarket explicitly provides its complete panel for embedding by a host. */
+export interface MarketHost {
+ readonly version: number;
+ readonly render: (props?: Record) => ReactNode;
+ readonly setSettingsVisible: (visible: boolean) => void;
+}
+
+export function communityMarketView(market: MarketHost) {
+ return function CommunityMarket({ view }: { readonly view: 'summary' | 'page' }): ReactNode {
+ if (view === 'summary') return ;
+ return market.render();
+ };
+}
diff --git a/workdsh-web/packages/bundle/src/client/harness/client.ts b/workdsh-web/packages/bundle/src/client/harness/client.ts
index 3725239d6a..041239718c 100644
--- a/workdsh-web/packages/bundle/src/client/harness/client.ts
+++ b/workdsh-web/packages/bundle/src/client/harness/client.ts
@@ -1,7 +1,9 @@
import { ShellAppearance } from '../components/ShellAppearance.js';
+import { communityMarketView, type MarketHost } from '../components/CommunityMarket.js';
import type { Context } from '@deepseek-ai/cordis';
import type {} from '@deepseek-ai/dsh-api-remotes/client';
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client';
+import type {} from '@deepseek-ai/dsh-client-ui-plugin-manager/client';
import type {} from '@deepseek-ai/dsh-client-ui-sidebar/client';
import type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client';
import type {} from '@deepseek-ai/dsh-client-ui-session/client';
@@ -13,6 +15,10 @@ import { DiagnosticsPanel, type Inventory } from '../components/DiagnosticsPanel
import { NavigationLocation } from '../components/NavigationLocation.js';
import { AgentBrowserPage, agentBrowserKind, readAgentBrowserFrame } from '../components/AgentBrowserPage.js';
+declare module '@deepseek-ai/cordis' {
+ interface Context { market: MarketHost; }
+}
+
declare module '@deepseek-ai/dsh-client-ui-sidebar-right/client' {
interface SidebarRightTabParamsMap { 'workdsh-agent-browser': Record; }
}
@@ -26,6 +32,15 @@ const productViews: Readonly> = {
};
export function apply(ctx: Context): void {
+ ctx.inject(['market'], scope => {
+ if (scope.market.version !== 1) return;
+ scope.market.setSettingsVisible(false);
+ scope.effect(() => () => scope.market.setSettingsVisible(true), 'workdsh.community-market.settings-visibility');
+ scope.slots.inject('plugins.item', () => scope.slots.register({
+ name: 'plugins.item', id: 'workdsh-community-market', order: -100,
+ label: '发现社区插件',
+ }, communityMarketView(scope.market)));
+ });
let legacyBrowserEnabled = false;
ctx.effect(() => {
const controller = new AbortController();
diff --git a/workdsh-web/packages/plugins/access/locale/en.json b/workdsh-web/packages/plugins/access/locale/en.json
new file mode 100644
index 0000000000..0a302ac879
--- /dev/null
+++ b/workdsh-web/packages/plugins/access/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Controls access to local resources, sessions and tools."
+ }
+}
diff --git a/workdsh-web/packages/plugins/access/locale/zh.json b/workdsh-web/packages/plugins/access/locale/zh.json
new file mode 100644
index 0000000000..6cdbcc2156
--- /dev/null
+++ b/workdsh-web/packages/plugins/access/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "管理本地资源、会话和工具的访问权限。"
+ }
+}
diff --git a/workdsh-web/packages/plugins/access/package.json b/workdsh-web/packages/plugins/access/package.json
index af207e688c..8a4849bc2d 100644
--- a/workdsh-web/packages/plugins/access/package.json
+++ b/workdsh-web/packages/plugins/access/package.json
@@ -15,13 +15,16 @@
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"cordis.patch.yml",
"dist",
"README.md",
- "CHANGELOG.md"
+ "CHANGELOG.md",
+ "locale/*.json"
],
"dsh": {
"bundle": {
@@ -50,5 +53,6 @@
"@deepseek-ai/dsh-session": "0.1.7-rc.2",
"@deepseek-ai/dsh-tools": "0.1.7-rc.2",
"workdsh-contracts": "workspace:*"
- }
+ },
+ "description": "Controls access to local resources, sessions and tools."
}
diff --git a/workdsh-web/packages/plugins/activity/locale/en.json b/workdsh-web/packages/plugins/activity/locale/en.json
new file mode 100644
index 0000000000..12e4de2d0e
--- /dev/null
+++ b/workdsh-web/packages/plugins/activity/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Shows session activity and agent collaboration status."
+ }
+}
diff --git a/workdsh-web/packages/plugins/activity/locale/zh.json b/workdsh-web/packages/plugins/activity/locale/zh.json
new file mode 100644
index 0000000000..eb3062aaf9
--- /dev/null
+++ b/workdsh-web/packages/plugins/activity/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "展示会话活动和智能体协作状态。"
+ }
+}
diff --git a/workdsh-web/packages/plugins/activity/package.json b/workdsh-web/packages/plugins/activity/package.json
index 060da4b64e..264db11f0d 100644
--- a/workdsh-web/packages/plugins/activity/package.json
+++ b/workdsh-web/packages/plugins/activity/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0-alpha.6",
"type": "module",
"private": false,
- "description": "Compact animated activity presentation for standard DSH sessions",
+ "description": "Shows session activity and agent collaboration status.",
"exports": {
".": {
"types": "./dist/index.d.ts",
@@ -16,14 +16,17 @@
"./presentation": {
"types": "./dist/activity.d.ts",
"default": "./dist/presentation.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"dist",
"README.md",
"cordis.patch.yml",
"DESIGN.md",
- "CHANGELOG.md"
+ "CHANGELOG.md",
+ "locale/*.json"
],
"scripts": {
"build": "corepack pnpm --filter workdsh-contracts build && tsc -p tsconfig.json && node ../../../scripts/build-activity.mjs",
diff --git a/workdsh-web/packages/plugins/audit/locale/en.json b/workdsh-web/packages/plugins/audit/locale/en.json
new file mode 100644
index 0000000000..32499e5116
--- /dev/null
+++ b/workdsh-web/packages/plugins/audit/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Records local management and execution events for audit."
+ }
+}
diff --git a/workdsh-web/packages/plugins/audit/locale/zh.json b/workdsh-web/packages/plugins/audit/locale/zh.json
new file mode 100644
index 0000000000..49e79d667f
--- /dev/null
+++ b/workdsh-web/packages/plugins/audit/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "记录本地管理与执行事件,供审计使用。"
+ }
+}
diff --git a/workdsh-web/packages/plugins/audit/package.json b/workdsh-web/packages/plugins/audit/package.json
index a3fe7cb902..d95debff00 100644
--- a/workdsh-web/packages/plugins/audit/package.json
+++ b/workdsh-web/packages/plugins/audit/package.json
@@ -7,13 +7,16 @@
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"cordis.patch.yml",
"dist",
"README.md",
- "CHANGELOG.md"
+ "CHANGELOG.md",
+ "locale/*.json"
],
"dsh": {
"bundle": {
@@ -36,5 +39,6 @@
"@deepseek-ai/dsh-storage-domain": "0.1.7-rc.2",
"@types/node": "22.19.0",
"workdsh-contracts": "workspace:*"
- }
+ },
+ "description": "Records local management and execution events for audit."
}
diff --git a/workdsh-web/packages/plugins/connectors/locale/en.json b/workdsh-web/packages/plugins/connectors/locale/en.json
new file mode 100644
index 0000000000..fd3012d852
--- /dev/null
+++ b/workdsh-web/packages/plugins/connectors/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Manages MCP connectors and per-conversation tool selection."
+ }
+}
diff --git a/workdsh-web/packages/plugins/connectors/locale/zh.json b/workdsh-web/packages/plugins/connectors/locale/zh.json
new file mode 100644
index 0000000000..a4d04711b7
--- /dev/null
+++ b/workdsh-web/packages/plugins/connectors/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "管理 MCP 连接器及每个对话可用的工具。"
+ }
+}
diff --git a/workdsh-web/packages/plugins/connectors/package.json b/workdsh-web/packages/plugins/connectors/package.json
index f79239479b..f7688b1d44 100644
--- a/workdsh-web/packages/plugins/connectors/package.json
+++ b/workdsh-web/packages/plugins/connectors/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0-alpha.4",
"private": false,
"type": "module",
- "description": "MCP connector management and a verified local example for WorkDSH",
+ "description": "Manages MCP connectors and per-conversation tool selection.",
"exports": {
".": {
"types": "./dist/index.d.ts",
@@ -12,13 +12,16 @@
"./client": {
"types": "./dist/client.d.ts",
"default": "./dist/client.browser.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"dist",
"README.md",
"CHANGELOG.md",
- "cordis.patch.yml"
+ "cordis.patch.yml",
+ "locale/*.json"
],
"scripts": {
"build": "corepack pnpm --filter workdsh-ui build && node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json && node ../../../scripts/build-connectors.mjs",
diff --git a/workdsh-web/packages/plugins/experts/locale/en.json b/workdsh-web/packages/plugins/experts/locale/en.json
new file mode 100644
index 0000000000..e92bc84f38
--- /dev/null
+++ b/workdsh-web/packages/plugins/experts/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Manages reusable experts and expert teams in WorkDSH."
+ }
+}
diff --git a/workdsh-web/packages/plugins/experts/locale/zh.json b/workdsh-web/packages/plugins/experts/locale/zh.json
new file mode 100644
index 0000000000..5a64d23494
--- /dev/null
+++ b/workdsh-web/packages/plugins/experts/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "管理 WorkDSH 中可复用的专家与专家团。"
+ }
+}
diff --git a/workdsh-web/packages/plugins/experts/package.json b/workdsh-web/packages/plugins/experts/package.json
index 508f440961..c777a7bf9d 100644
--- a/workdsh-web/packages/plugins/experts/package.json
+++ b/workdsh-web/packages/plugins/experts/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0-alpha.9",
"private": false,
"type": "module",
- "description": "Local expert management plugin for DeepSeek Harness (D04 / P1-02, expert module 0.1)",
+ "description": "Manages reusable experts and expert teams in WorkDSH.",
"exports": {
".": {
"types": "./dist/index.d.ts",
@@ -12,14 +12,17 @@
"./client": {
"types": "./dist/client.d.ts",
"default": "./dist/client.browser.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"dist",
"resources",
"README.md",
"CHANGELOG.md",
- "cordis.patch.yml"
+ "cordis.patch.yml",
+ "locale/*.json"
],
"scripts": {
"build": "corepack pnpm --filter workdsh-contracts build && corepack pnpm --filter workdsh-ui build && node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json && node ../../../scripts/build-experts.mjs",
diff --git a/workdsh-web/packages/plugins/library/locale/en.json b/workdsh-web/packages/plugins/library/locale/en.json
new file mode 100644
index 0000000000..95083e0a62
--- /dev/null
+++ b/workdsh-web/packages/plugins/library/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Organizes local documents, revisions and task resources."
+ }
+}
diff --git a/workdsh-web/packages/plugins/library/locale/zh.json b/workdsh-web/packages/plugins/library/locale/zh.json
new file mode 100644
index 0000000000..61387bd062
--- /dev/null
+++ b/workdsh-web/packages/plugins/library/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "整理本地文档、修订版本和任务资料。"
+ }
+}
diff --git a/workdsh-web/packages/plugins/library/package.json b/workdsh-web/packages/plugins/library/package.json
index f61c294320..067256d975 100644
--- a/workdsh-web/packages/plugins/library/package.json
+++ b/workdsh-web/packages/plugins/library/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0-alpha.5",
"private": false,
"type": "module",
- "description": "Local asset library, deterministic document conversion and task reuse for WorkDSH",
+ "description": "Organizes local documents, revisions and task resources.",
"exports": {
".": {
"types": "./dist/index.d.ts",
@@ -12,13 +12,16 @@
"./client": {
"types": "./dist/client.d.ts",
"default": "./dist/client.browser.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"dist",
"README.md",
"CHANGELOG.md",
- "cordis.patch.yml"
+ "cordis.patch.yml",
+ "locale/*.json"
],
"scripts": {
"build": "corepack pnpm --filter workdsh-contracts build && corepack pnpm --filter workdsh-ui build && tsc -p tsconfig.json && node ../../../scripts/build-library.mjs",
diff --git a/workdsh-web/packages/plugins/office/locale/en.json b/workdsh-web/packages/plugins/office/locale/en.json
new file mode 100644
index 0000000000..c910f13864
--- /dev/null
+++ b/workdsh-web/packages/plugins/office/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Creates and edits document working copies in WorkDSH."
+ }
+}
diff --git a/workdsh-web/packages/plugins/office/locale/zh.json b/workdsh-web/packages/plugins/office/locale/zh.json
new file mode 100644
index 0000000000..493a5bdf24
--- /dev/null
+++ b/workdsh-web/packages/plugins/office/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "在 WorkDSH 中创建和编辑文档工作副本。"
+ }
+}
diff --git a/workdsh-web/packages/plugins/office/package.json b/workdsh-web/packages/plugins/office/package.json
index 8b3df66bc9..974e73cb5e 100644
--- a/workdsh-web/packages/plugins/office/package.json
+++ b/workdsh-web/packages/plugins/office/package.json
@@ -9,14 +9,17 @@
},
"./client": {
"default": "./dist/client.browser.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"dist",
"cordis.patch.yml",
"README.md",
"THIRD-PARTY-NOTICES.md",
- "CHANGELOG.md"
+ "CHANGELOG.md",
+ "locale/*.json"
],
"scripts": {
"build": "node ../../../scripts/build-office.mjs",
@@ -108,5 +111,6 @@
"@deepseek-ai/dsh-fs": {
"optional": true
}
- }
+ },
+ "description": "Creates and edits document working copies in WorkDSH."
}
diff --git a/workdsh-web/packages/plugins/projects/locale/en.json b/workdsh-web/packages/plugins/projects/locale/en.json
new file mode 100644
index 0000000000..98e4ed8cb0
--- /dev/null
+++ b/workdsh-web/packages/plugins/projects/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Organizes tasks, configurations and assets by project."
+ }
+}
diff --git a/workdsh-web/packages/plugins/projects/locale/zh.json b/workdsh-web/packages/plugins/projects/locale/zh.json
new file mode 100644
index 0000000000..90470d4d5b
--- /dev/null
+++ b/workdsh-web/packages/plugins/projects/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "按项目组织任务、配置和资料。"
+ }
+}
diff --git a/workdsh-web/packages/plugins/projects/package.json b/workdsh-web/packages/plugins/projects/package.json
index 7b0a095839..229953fe3f 100644
--- a/workdsh-web/packages/plugins/projects/package.json
+++ b/workdsh-web/packages/plugins/projects/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0-alpha.4",
"private": false,
"type": "module",
- "description": "Project workspace, planning, configuration and asset references for WorkDSH",
+ "description": "Organizes tasks, configurations and assets by project.",
"exports": {
".": {
"types": "./dist/index.d.ts",
@@ -12,13 +12,16 @@
"./client": {
"types": "./dist/client.d.ts",
"default": "./dist/client.browser.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"dist",
"README.md",
"CHANGELOG.md",
- "cordis.patch.yml"
+ "cordis.patch.yml",
+ "locale/*.json"
],
"scripts": {
"build": "corepack pnpm --filter workdsh-contracts build && corepack pnpm --filter workdsh-ui build && tsc -p tsconfig.json && node ../../../scripts/build-projects.mjs",
diff --git a/workdsh-web/packages/plugins/skills/locale/en.json b/workdsh-web/packages/plugins/skills/locale/en.json
new file mode 100644
index 0000000000..24046b0c02
--- /dev/null
+++ b/workdsh-web/packages/plugins/skills/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Manages installed skills, local skill packages and SkillHub browsing."
+ }
+}
diff --git a/workdsh-web/packages/plugins/skills/locale/zh.json b/workdsh-web/packages/plugins/skills/locale/zh.json
new file mode 100644
index 0000000000..2bb873ff53
--- /dev/null
+++ b/workdsh-web/packages/plugins/skills/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "管理已安装技能、本地技能包和 SkillHub 浏览。"
+ }
+}
diff --git a/workdsh-web/packages/plugins/skills/package.json b/workdsh-web/packages/plugins/skills/package.json
index 00b515bb32..a5fbce1207 100644
--- a/workdsh-web/packages/plugins/skills/package.json
+++ b/workdsh-web/packages/plugins/skills/package.json
@@ -1,6 +1,6 @@
{
"name": "workdsh-plugin-skills",
- "version": "0.1.0-alpha.33",
+ "version": "0.1.0-alpha.34",
"private": false,
"type": "module",
"exports": {
@@ -11,14 +11,17 @@
"./client": {
"types": "./dist/client.d.ts",
"default": "./dist/client.browser.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"dist",
"resources",
"README.md",
"CHANGELOG.md",
- "cordis.patch.yml"
+ "cordis.patch.yml",
+ "locale/*.json"
],
"scripts": {
"build": "node ../../../scripts/generate-builtin-skills.mjs && corepack pnpm --filter workdsh-contracts build && corepack pnpm --filter workdsh-ui build && tsc -p tsconfig.json && node ../../../scripts/build-skills.mjs",
@@ -57,7 +60,7 @@
"fflate": "0.8.3",
"yaml": "2.9.0"
},
- "description": "Local skill management plugin for DeepSeek Harness",
+ "description": "Manages installed skills, local skill packages and SkillHub browsing.",
"dsh": {
"bundle": {
"patch": "./cordis.patch.yml"
diff --git a/workdsh-web/packages/plugins/skills/src/client/SkillHubPanel.tsx b/workdsh-web/packages/plugins/skills/src/client/SkillHubPanel.tsx
new file mode 100644
index 0000000000..dbe904f86e
--- /dev/null
+++ b/workdsh-web/packages/plugins/skills/src/client/SkillHubPanel.tsx
@@ -0,0 +1,88 @@
+import * as React from 'react';
+import { useEffect, useState } from 'react';
+import { Button } from 'workdsh-ui';
+
+type SkillHubCard = {
+ slug: string;
+ name: string;
+ description: string;
+ categoryLabel?: string;
+ version?: string;
+ owner?: string;
+ iconUrl?: string;
+ pageUrl?: string;
+ installed?: boolean;
+};
+
+type SearchResponse = { ok: boolean; items?: SkillHubCard[]; total?: number; error?: string };
+const PAGE_SIZE = 24;
+
+function SkillHubIcon({ card }: { card: SkillHubCard }) {
+ const iconUrl = card.iconUrl && /^https:\/\//i.test(card.iconUrl) ? card.iconUrl : undefined;
+ return {card.name.slice(0, 1).toUpperCase()}{iconUrl && { event.currentTarget.hidden = true; }} />} ;
+}
+
+async function skillHub(method: string, payload: Record, signal?: AbortSignal): Promise {
+ const response = await fetch(new URL('./skillhub', document.baseURI), {
+ method: 'POST', credentials: 'same-origin',
+ headers: { 'content-type': 'application/json' },
+ body: JSON.stringify({ method, ...payload }), signal,
+ });
+ const result = await response.json() as T;
+ if (!response.ok || !result.ok) throw new Error(result.error || `SkillHub 请求失败 (${response.status})`);
+ return result;
+}
+
+/** Reuse the installed DSH plugin's search and verified installation path. */
+export function SkillHubPanel({ query, onInstalled, onOpenInstalled }: { query: string; onInstalled: () => Promise; onOpenInstalled: () => void }) {
+ const [cards, setCards] = useState([]);
+ const [total, setTotal] = useState(0);
+ const [busy, setBusy] = useState(true);
+ const [error, setError] = useState('');
+ const [installing, setInstalling] = useState('');
+ const [installedName, setInstalledName] = useState('');
+ const [page, setPage] = useState(0);
+
+ useEffect(() => { setPage(0); }, [query]);
+
+ useEffect(() => {
+ const controller = new AbortController();
+ const timer = window.setTimeout(() => {
+ setBusy(true); setError('');
+ void skillHub('search', { query: query.trim(), limit: PAGE_SIZE, offset: page * PAGE_SIZE }, controller.signal)
+ .then(result => { setCards([...new Map((result.items ?? []).map(item => [item.slug, item])).values()]); setTotal(result.total ?? 0); })
+ .catch(cause => { if (!controller.signal.aborted) setError(cause instanceof Error ? cause.message : 'SkillHub 暂不可用'); })
+ .finally(() => { if (!controller.signal.aborted) setBusy(false); });
+ }, 250);
+ return () => { window.clearTimeout(timer); controller.abort(); };
+ }, [query, page]);
+
+ const install = async (card: SkillHubCard) => {
+ setInstalling(card.slug); setError('');
+ try {
+ await skillHub('install', { slug: card.slug });
+ setCards(current => current.map(item => item.slug === card.slug ? { ...item, installed: true } : item));
+ setInstalledName(card.name);
+ void onInstalled();
+ } catch (cause) { setError(cause instanceof Error ? cause.message : '安装失败,请重试。'); }
+ finally { setInstalling(''); }
+ };
+
+ const pageCount = Math.ceil(total / PAGE_SIZE);
+ const firstPage = Math.max(0, Math.min(page - 2, pageCount - 5));
+ const pageNumbers = Array.from({ length: Math.min(5, pageCount) }, (_, index) => firstPage + index);
+
+ return
+
SkillHub {total} 来源:SkillHub · 安装到本机 DSH 技能目录
+ {installedName && 已安装「{installedName}」。 查看已安装 setInstalledName('')}>×
}
+ {error && {error}
}
+ {busy ? 正在读取 SkillHub…
: cards.length ? {cards.map(card =>
+
+
+ {card.description}
+ 版本 {card.version || '未标注'} · 许可证请查看来源页
+ )}
: !error && 没有找到匹配的技能。
}
+ {!busy && pageCount > 1 && setPage(value => value - 1)}>上一页 {firstPage > 0 && … }{pageNumbers.map(number => setPage(number)}>{number + 1} )}{firstPage + pageNumbers.length < pageCount && … }= pageCount} onClick={() => setPage(value => value + 1)}>下一页 第 {page + 1} / {pageCount} 页 }
+ ;
+}
diff --git a/workdsh-web/packages/plugins/skills/src/client/SkillsPanel.tsx b/workdsh-web/packages/plugins/skills/src/client/SkillsPanel.tsx
index 9f1fb3c2cd..e67e9d4120 100644
--- a/workdsh-web/packages/plugins/skills/src/client/SkillsPanel.tsx
+++ b/workdsh-web/packages/plugins/skills/src/client/SkillsPanel.tsx
@@ -7,6 +7,7 @@ import type { ManagedSkillDetail, ManagedSkillResource, ManagedSkillSummary, Ski
import type { SkillTaskKind } from './drafts.js';
import type { SkillManagementClient } from './management.js';
import { ImportSkillModal } from './ImportSkillModal.js';
+import { SkillHubPanel } from './SkillHubPanel.js';
import { skillsActionsCss, skillsCss, skillsMarketCss } from './styles.js';
type SkillsPanelInjected = {
@@ -65,6 +66,7 @@ export function SkillsPanel({ toggleNavigation, management, startSkillTask, star
const [selectedNames, setSelectedNames] = useState([]);
const [confirmBatchUninstall, setConfirmBatchUninstall] = useState(false);
const [view, setView] = useState<'market' | 'installed'>('market');
+ const [marketSource, setMarketSource] = useState<'local' | 'skillhub'>('local');
const [installedQuery, setInstalledQuery] = useState('');
const search = useRef(null);
const addMenu = useRef(null);
@@ -205,12 +207,15 @@ export function SkillsPanel({ toggleNavigation, management, startSkillTask, star
};
const entries = catalog?.entries ?? [];
- const categories = catalog?.categories ?? [];
const normalized = query.trim().toLowerCase();
const matches = (text: string) => text.toLowerCase().includes(normalized);
- const inCategory = (values?: readonly string[]) => category === ALL || Boolean(values?.includes(category));
- const available = entries.filter(entry => !entry.installed && inCategory(entry.categories) && (matches(entry.name) || matches(entry.title) || matches(entry.description)));
- const filtered = skills.filter(skill => inCategory(skill.categories) && matches(`${skill.name} ${skill.title ?? ''} ${skill.localizedDescription ?? skill.description} ${skill.whenToUse ?? ''}`));
+ const matchingEntries = entries.filter(entry => !entry.installed && (matches(entry.name) || matches(entry.title) || matches(entry.description)));
+ const matchingSkills = skills.filter(skill => matches(`${skill.name} ${skill.title ?? ''} ${skill.localizedDescription ?? skill.description} ${skill.whenToUse ?? ''}`));
+ const categories = [...new Set([...matchingEntries, ...matchingSkills].flatMap(item => item.categories ?? []))].sort((a, b) => a.localeCompare(b, 'zh-CN'));
+ const selectedCategory = categories.includes(category) ? category : ALL;
+ const inCategory = (values?: readonly string[]) => selectedCategory === ALL || Boolean(values?.includes(selectedCategory));
+ const available = matchingEntries.filter(entry => inCategory(entry.categories));
+ const filtered = matchingSkills.filter(skill => inCategory(skill.categories));
const installedFiltered = skills.filter(skill => `${skill.name} ${skill.title ?? ''} ${skill.localizedDescription ?? skill.description} ${skill.whenToUse ?? ''}`.toLowerCase().includes(installedQuery.trim().toLowerCase()));
const capabilityTabs = [['experts', '专家'], ['skills', '技能'], ['connectors', '连接器']] as const;
const capabilityKey: Record = { experts: 'workdsh-experts', skills: 'workdsh-skills', connectors: 'workdsh-connectors' };
@@ -258,7 +263,9 @@ export function SkillsPanel({ toggleNavigation, management, startSkillTask, star
setAddMenuOpen(open => !open)}>+ 添加技能 {addMenuOpen &&
{ setAddMenuOpen(false); search.current?.focus(); }}>查找技能 { setAddMenuOpen(false); setImportOpen(true); }}>上传技能 void beginSkillTask('create')}>创建技能
}
技能市场 void refresh()} disabled={busy}>刷新
- setCategory(ALL)}>全部 {categories.map(label => setCategory(current => current === label ? ALL : label)}>{label} )}
+ setMarketSource('local')}>本地技能 setMarketSource('skillhub')}>SkillHub
+ {marketSource === 'skillhub' ? : <>
+ setCategory(ALL)}>全部 {categories.map(label => setCategory(current => current === label ? ALL : label)}>{label} )}
{catalog?.status === 'invalid' && 技能目录暂时不可用,已安装的技能仍可使用。
}
{batchMode && batchBar}
{countsLine}
@@ -274,6 +281,7 @@ export function SkillsPanel({ toggleNavigation, management, startSkillTask, star
{filtered.map(renderSkillCard)}
: null}
>}
+ >}
setPreview(undefined)}>
{preview && {preview.title} {preview.name}
void install(preview)}>{installBusy === preview.name ? '正在安装…' : '+ 安装'} setPreview(undefined)}>稍后再说
diff --git a/workdsh-web/packages/plugins/skills/src/client/styles.ts b/workdsh-web/packages/plugins/skills/src/client/styles.ts
index 55a208026e..cd535d1bce 100644
--- a/workdsh-web/packages/plugins/skills/src/client/styles.ts
+++ b/workdsh-web/packages/plugins/skills/src/client/styles.ts
@@ -105,6 +105,25 @@ ${controlsCss}
export const skillsMarketCss = `${modalCss}
@layer workdsh-business {
+.wd-skills .section-head:has(+ .skill-source-tabs){margin-bottom:0;border-bottom:0}
+.wd-skills .skill-source-tabs{display:flex;gap:24px;margin:0 0 20px;border-bottom:1px solid var(--dsw-alias-border-l2)}
+.wd-skills .skill-source-tabs button{padding:10px 2px;border:0;border-bottom:2px solid transparent;border-radius:0;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit}
+.wd-skills .skill-source-tabs button:hover:not(:disabled){background:transparent;color:var(--dsw-alias-label-primary)}
+.wd-skills .skill-source-tabs button.active{border-bottom-color:var(--dsw-alias-brand-primary);background:transparent;color:var(--dsw-alias-label-primary);font-weight:600}
+.wd-skills .skillhub-market .card-open{text-decoration:none;color:inherit}
+.wd-skills .skillhub-icon{position:relative;display:grid;place-items:center;width:46px;height:46px;flex:none;overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-brand-text);font-weight:700;font-size:19px}
+.wd-skills .skillhub-icon img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:var(--dsw-alias-bg-layer-2)}
+.wd-skills .skillhub-icon img[hidden]{display:none}
+.wd-skills .skillhub-meta{display:block;margin-top:8px;color:var(--dsw-alias-label-tertiary);font-size:11px}
+.wd-skills .skillhub-market .install.is-status{width:auto;min-width:58px;padding:0 8px;border-radius:8px;font-size:12px}
+.wd-skills .skillhub-market .install.is-installed:disabled{color:var(--dsw-alias-state-success-primary);opacity:1}
+.wd-skills .skillhub-success{display:flex;align-items:center;gap:12px;margin:0 0 18px;padding:10px 14px;border:1px solid var(--dsw-alias-state-success-primary);border-radius:10px;background:var(--dsw-alias-bg-layer-2);font-size:13px}
+.wd-skills .skillhub-success span{flex:1}
+.wd-skills .skillhub-success .skillhub-dismiss{border:0;background:transparent;font-size:18px}
+.wd-skills .skillhub-pagination{display:flex;justify-content:center;align-items:center;gap:8px;flex-wrap:wrap;margin:28px 0 12px}
+.wd-skills .skillhub-pagination button{min-width:36px;background:transparent}
+.wd-skills .skillhub-pagination button.active{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-text);font-weight:600}
+
.wd-skills .skill-icon{width:46px;flex:none;object-fit:cover}
diff --git a/workdsh-web/packages/plugins/skills/src/services/manager.ts b/workdsh-web/packages/plugins/skills/src/services/manager.ts
index 1d7b9de44b..4d7ea07750 100644
--- a/workdsh-web/packages/plugins/skills/src/services/manager.ts
+++ b/workdsh-web/packages/plugins/skills/src/services/manager.ts
@@ -162,7 +162,7 @@ export class SkillManager extends Service implements SkillManagementService {
constructor(ctx: Context) {
super(ctx, 'workdshSkills');
const dshHome = resolve(process.env.DSH_HOME ?? join(homedir(), '.dsh'));
- const agentsHome = resolve(process.env.DSH_AGENTS_HOME ?? join(homedir(), '.agents'));
+ const agentsHome = resolve(process.env.DSH_AGENTS_HOME ?? join(dshHome, 'agents'));
this.activeRoots = [join(agentsHome, 'skills'), join(dshHome, 'skills')];
this.disabledRoot = join(agentsHome, '.workdsh-disabled', 'skills');
this.trashRoot = join(agentsHome, '.workdsh-trash', 'skills');
@@ -183,13 +183,14 @@ export class SkillManager extends Service implements SkillManagementService {
signal?.throwIfAborted();
const skills = await this.ctx.skills.list({ signal });
const rows: ManagedSkillSummary[] = [];
+ const registeredFiles = new Set();
for (const skill of skills) {
const manageable = this.isManagedSummary(skill);
if (manageable) {
const definition = await this.ctx.skills.get(skill.name, { signal });
const path = definition?.path;
if (!path) continue;
- try { if (!await this.isSafeManagedFile(path)) continue; }
+ try { if (!await this.isSafeManagedFile(path)) continue; registeredFiles.add(await realpath(path)); }
catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT' || (error as Error).message === 'skill/path-symlink') continue; throw error; }
}
rows.push({ name: skill.name, description: skill.description, whenToUse: skill.whenToUse,
@@ -204,10 +205,13 @@ export class SkillManager extends Service implements SkillManagementService {
try { active = await this.activeEntry(name); }
catch (error) { if ((error as Error).message === 'skill/path-symlink') continue; throw error; }
if (!active) continue;
+ if (registeredFiles.has(await realpath(active.file))) continue;
const document = await readFile(active.file, 'utf8');
- const validation = this.validateDocument(document, name);
+ const validation = this.validateDocument(document);
+ const displayName = validation.valid ? validation.name ?? name : name;
+ if (rows.some(row => row.name === displayName)) continue;
rows.push({
- name,
+ name: displayName,
description: validation.description ?? '技能文件需要修复',
whenToUse: frontmatterValue(document, 'when-to-use'),
modelInvocable: validation.valid,
@@ -246,7 +250,7 @@ export class SkillManager extends Service implements SkillManagementService {
const active = await this.activeEntry(name);
if (active) {
const document = await readFile(active.file, 'utf8');
- const validation = this.validateDocument(document, name);
+ const validation = this.validateDocument(document);
return {
name,
description: validation.description ?? '技能文件需要修复',
@@ -291,17 +295,19 @@ export class SkillManager extends Service implements SkillManagementService {
validateDocument(document: string, expectedName?: string): SkillValidationResult {
const diagnostics: SkillDiagnostic[] = [];
- if (Buffer.byteLength(document) > maximumDocumentBytes) diagnostics.push({ code: 'document-too-large', message: 'SKILL.md 超过 1 MiB 上限。', path: 'SKILL.md' });
+ if (expectedName && Buffer.byteLength(document) > maximumDocumentBytes) diagnostics.push({ code: 'document-too-large', message: 'SKILL.md 超过 1 MiB 上限。', path: 'SKILL.md' });
let metadata: Record | undefined;
try { metadata = frontmatter(document); }
catch { diagnostics.push({ code: 'invalid-frontmatter', message: 'SKILL.md 必须以有效的 YAML frontmatter 开头。', path: 'SKILL.md' }); }
const name = typeof metadata?.name === 'string' ? metadata.name.trim() : undefined;
const description = typeof metadata?.description === 'string' ? metadata.description.trim() : undefined;
if (!name || !skillNamePattern.test(name) || !isSkillName(name)) diagnostics.push({ code: 'invalid-name', message: 'name 必须是合法的 kebab-case 技能名称。', path: 'SKILL.md' });
- if (expectedName && name && name !== expectedName) diagnostics.push({ code: 'name-mismatch', message: `frontmatter name 必须与技能目录 ${expectedName} 一致。`, path: 'SKILL.md' });
+ // The official DSH loader identifies a skill by frontmatter name, not its directory.
+ // A marketplace slug may differ; only authoring flows enforce an expected name.
+ if (expectedName && name && name !== expectedName) diagnostics.push({ code: 'name-mismatch', message: `frontmatter name 必须是 ${expectedName}。`, path: 'SKILL.md' });
if (!description) diagnostics.push({ code: 'description-required', message: 'description 不能为空。', path: 'SKILL.md' });
const body = metadata ? document.replace(/^---\s*\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/, '').trim() : '';
- if (metadata && !body) diagnostics.push({ code: 'instructions-required', message: 'frontmatter 后必须包含可执行的技能说明。', path: 'SKILL.md' });
+ if (expectedName && metadata && !body) diagnostics.push({ code: 'instructions-required', message: 'frontmatter 后必须包含可执行的技能说明。', path: 'SKILL.md' });
return { valid: diagnostics.length === 0, ...(name ? { name } : {}), ...(description ? { description } : {}), diagnostics };
}
@@ -451,7 +457,7 @@ export class SkillManager extends Service implements SkillManagementService {
if (!current?.directoryPath || (current.state !== 'enabled' && current.state !== 'invalid')) throw new Error('skill/not-manageable');
const active = await this.activeEntry(name);
if (!active) throw new Error('skill/not-manageable');
- const target = join(this.disabledRoot, basename(active.entry));
+ const target = join(this.disabledRoot, active.directoryBundle ? name : `${name}.md`);
await this.assertAbsent(target); await mkdir(this.disabledRoot, { recursive: true });
await this.writeJson(this.originPath(name), { name, originalEntry: active.entry, directoryBundle: active.directoryBundle } satisfies DisabledOrigin);
try { await rename(active.entry, target); }
@@ -591,7 +597,7 @@ export class SkillManager extends Service implements SkillManagementService {
modelInvocable: definition.invocation.modelInvocable, state: 'readonly', manageable: false, resources: [],
};
if (!definition.path) return readonlyDetail;
- const active = await this.activeEntry(definition.name);
+ const active = await this.managedEntryForPath(definition.path);
if (!active) return readonlyDetail;
// Harness exposes a canonical instruction path; configured roots may use
// an OS alias (e.g. /var -> /private/var). Compare actual files only after
@@ -649,6 +655,42 @@ export class SkillManager extends Service implements SkillManagementService {
} catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
}
}
+ const definition = await this.ctx.skills.get(name);
+ if (definition?.path) return this.managedEntryForPath(definition.path);
+ // Web Profile loads filesystem skills in Agent compositions rather than the
+ // top-level registry. Resolve the installed skill's declared name locally.
+ for (const root of this.activeRoots) {
+ await mkdir(root, { recursive: true });
+ for (const row of await readdir(root, { withFileTypes: true })) {
+ if (!row.isDirectory() || !skillNamePattern.test(row.name)) continue;
+ const entry = join(root, row.name);
+ const file = join(entry, 'SKILL.md');
+ try {
+ if ((await lstat(entry)).isSymbolicLink() || (await lstat(file)).isSymbolicLink() || !await this.isSafeManagedFile(file)) continue;
+ if (frontmatterValue(await readFile(file, 'utf8'), 'name') === name) return { entry, file, directoryBundle: true };
+ } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
+ }
+ }
+ return undefined;
+ }
+
+ private async managedEntryForPath(path: string): Promise<{ entry: string; file: string; directoryBundle: boolean } | undefined> {
+ if (!await this.isSafeManagedFile(path)) return undefined;
+ const canonical = await realpath(path);
+ for (const root of this.activeRoots) {
+ let canonicalRoot: string;
+ try { canonicalRoot = await realpath(root); }
+ catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') continue; throw error; }
+ const part = relative(canonicalRoot, canonical);
+ const segments = part.split(sep);
+ const directoryBundle = segments.length === 2 && segments[1] === 'SKILL.md' && skillNamePattern.test(segments[0]);
+ const standalone = segments.length === 1 && segments[0].endsWith('.md') && skillNamePattern.test(segments[0].slice(0, -3));
+ if (!directoryBundle && !standalone) continue;
+ const entry = join(root, segments[0]);
+ const file = directoryBundle ? join(entry, 'SKILL.md') : entry;
+ if ((await lstat(entry)).isSymbolicLink() || (await lstat(file)).isSymbolicLink()) throw new Error('skill/path-symlink');
+ if (await realpath(file) === canonical) return { entry, file, directoryBundle };
+ }
return undefined;
}
diff --git a/workdsh-web/packages/plugins/workbench/locale/en.json b/workdsh-web/packages/plugins/workbench/locale/en.json
new file mode 100644
index 0000000000..6ed007d667
--- /dev/null
+++ b/workdsh-web/packages/plugins/workbench/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Adds WorkDSH navigation and workspace entry points to the DSH sidebar."
+ }
+}
diff --git a/workdsh-web/packages/plugins/workbench/locale/zh.json b/workdsh-web/packages/plugins/workbench/locale/zh.json
new file mode 100644
index 0000000000..5bace524fe
--- /dev/null
+++ b/workdsh-web/packages/plugins/workbench/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "在 DSH 侧栏加入 WorkDSH 导航与工作区入口。"
+ }
+}
diff --git a/workdsh-web/packages/plugins/workbench/package.json b/workdsh-web/packages/plugins/workbench/package.json
index 3311135096..ac3f5da0a5 100644
--- a/workdsh-web/packages/plugins/workbench/package.json
+++ b/workdsh-web/packages/plugins/workbench/package.json
@@ -7,12 +7,15 @@
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"dist",
"README.md",
- "CHANGELOG.md"
+ "CHANGELOG.md",
+ "locale/*.json"
],
"scripts": {
"build": "tsc -p tsconfig.json",
@@ -38,5 +41,6 @@
"@deepseek-ai/dsh-client-ui-session": "0.1.7-rc.2",
"@deepseek-ai/dsh-client-ui-chat": "0.1.7-rc.2",
"@deepseek-ai/dsh-tool-todo": "0.1.7-rc.2"
- }
+ },
+ "description": "Adds WorkDSH navigation and workspace entry points to the DSH sidebar."
}
diff --git a/workdsh-web/packages/providers/browser-session/locale/en.json b/workdsh-web/packages/providers/browser-session/locale/en.json
new file mode 100644
index 0000000000..c553fc0269
--- /dev/null
+++ b/workdsh-web/packages/providers/browser-session/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Connects the current agent session to a managed browser."
+ }
+}
diff --git a/workdsh-web/packages/providers/browser-session/locale/zh.json b/workdsh-web/packages/providers/browser-session/locale/zh.json
new file mode 100644
index 0000000000..5ed2a7bac4
--- /dev/null
+++ b/workdsh-web/packages/providers/browser-session/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "将当前智能体会话连接到受管浏览器。"
+ }
+}
diff --git a/workdsh-web/packages/providers/browser-session/package.json b/workdsh-web/packages/providers/browser-session/package.json
index 6b3b6d2962..989d1727eb 100644
--- a/workdsh-web/packages/providers/browser-session/package.json
+++ b/workdsh-web/packages/providers/browser-session/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0-alpha.2",
"private": true,
"type": "module",
- "description": "Session-owned Chromium browser bridge for WorkDSH",
+ "description": "Connects the current agent session to a managed browser.",
"exports": {
".": {
"types": "./dist/index.d.ts",
@@ -12,9 +12,15 @@
"./client": {
"types": "./dist/client.d.ts",
"default": "./dist/client.browser.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
- "files": ["dist", "README.md"],
+ "files": [
+ "dist",
+ "README.md",
+ "locale/*.json"
+ ],
"scripts": {
"build": "tsc -p tsconfig.json && node ../../../scripts/build-browser-session.mjs",
"typecheck": "tsc -p tsconfig.json --noEmit",
diff --git a/workdsh-web/packages/providers/identity-local/locale/en.json b/workdsh-web/packages/providers/identity-local/locale/en.json
new file mode 100644
index 0000000000..0cf73caafa
--- /dev/null
+++ b/workdsh-web/packages/providers/identity-local/locale/en.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "Provides trusted local identity and personal workspace ownership."
+ }
+}
diff --git a/workdsh-web/packages/providers/identity-local/locale/zh.json b/workdsh-web/packages/providers/identity-local/locale/zh.json
new file mode 100644
index 0000000000..50be1ee2a1
--- /dev/null
+++ b/workdsh-web/packages/providers/identity-local/locale/zh.json
@@ -0,0 +1,5 @@
+{
+ "meta": {
+ "description": "提供可信本地身份与个人工作区归属信息。"
+ }
+}
diff --git a/workdsh-web/packages/providers/identity-local/package.json b/workdsh-web/packages/providers/identity-local/package.json
index 4fbe8342c7..4e6bf72cf5 100644
--- a/workdsh-web/packages/providers/identity-local/package.json
+++ b/workdsh-web/packages/providers/identity-local/package.json
@@ -7,13 +7,16 @@
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- }
+ },
+ "./package.json": "./package.json",
+ "./locale/*.json": "./locale/*.json"
},
"files": [
"dist",
"README.md",
"CHANGELOG.md",
- "cordis.patch.yml"
+ "cordis.patch.yml",
+ "locale/*.json"
],
"dsh": {
"bundle": {
@@ -37,5 +40,6 @@
"@deepseek-ai/dsh-storage-domain": "0.1.7-rc.2",
"@types/node": "22.19.0",
"workdsh-contracts": "workspace:*"
- }
+ },
+ "description": "Provides trusted local identity and personal workspace ownership."
}
diff --git a/workdsh-web/pnpm-lock.yaml b/workdsh-web/pnpm-lock.yaml
index 9c638c52eb..d570c0a66e 100644
--- a/workdsh-web/pnpm-lock.yaml
+++ b/workdsh-web/pnpm-lock.yaml
@@ -440,6 +440,9 @@ importers:
'@deepseek-ai/dsh-client-ui-layout':
specifier: 0.1.7-rc.2
version: 0.1.7-rc.2(@deepseek-ai/cordis@4.0.3)
+ '@deepseek-ai/dsh-client-ui-plugin-manager':
+ specifier: 0.1.7-rc.2
+ version: 0.1.7-rc.2(@deepseek-ai/cordis@4.0.3)
'@deepseek-ai/dsh-client-ui-renderer':
specifier: 0.1.7-rc.2
version: 0.1.7-rc.2(@deepseek-ai/cordis@4.0.3)
diff --git a/workdsh-web/scripts/build-skill-catalog.mjs b/workdsh-web/scripts/build-skill-catalog.mjs
index e9b1fcbe00..ee6625db7d 100644
--- a/workdsh-web/scripts/build-skill-catalog.mjs
+++ b/workdsh-web/scripts/build-skill-catalog.mjs
@@ -10,7 +10,7 @@
//
// Usage:
// node scripts/build-skill-catalog.mjs --source /path/to/skills-marketplace
-// node scripts/build-skill-catalog.mjs --source ... --target ~/.agents/.workdsh-catalog --dry-run
+// node scripts/build-skill-catalog.mjs --source ... --target ~/.dsh/agents/.workdsh-catalog --dry-run
import { createHash } from 'node:crypto';
import { copyFile, lstat, mkdir, readdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os';
@@ -47,7 +47,7 @@ function parseArguments(argv) {
if (!options.source) throw new Error('缺少 --source:请指向包含 .codebuddy-skill/marketplace.json、skills/ 与 icons/ 的镜像目录。');
options.source = resolve(options.source.replace(/^~(?=\/|$)/, homedir()));
if (!options.target) {
- const agentsHome = resolve(process.env.DSH_AGENTS_HOME ?? join(homedir(), '.agents'));
+ const agentsHome = resolve(process.env.DSH_AGENTS_HOME ?? join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'agents'));
options.target = join(agentsHome, '.workdsh-catalog');
}
options.target = resolve(options.target.replace(/^~(?=\/|$)/, homedir()));
diff --git a/workdsh-web/scripts/install-preview.mjs b/workdsh-web/scripts/install-preview.mjs
index 868a534d62..642574c751 100644
--- a/workdsh-web/scripts/install-preview.mjs
+++ b/workdsh-web/scripts/install-preview.mjs
@@ -16,6 +16,8 @@ if (typeof baseVersion !== 'string') throw new Error('Missing pinned @deepseek-a
if (typeof webAppVersion !== 'string') throw new Error('Missing pinned @deepseek-ai/dsh-web-app version in package.json pnpm.overrides.');
const baseSpec = `@deepseek-ai/dsh-base@${baseVersion}`;
const webAppSpec = `@deepseek-ai/dsh-web-app@${webAppVersion}`;
+const marketVersion = '1.66.1';
+const marketSpec = `dshmarket@${marketVersion}`;
const cliVersion = JSON.parse(await readFile(join(root, 'node_modules/@deepseek-ai/dsh/package.json'), 'utf8')).version;
if (cliVersion !== baseVersion) throw new Error('Preview CLI and Base must use the same pinned version.');
const home = resolve(process.env.WORKDSH_PREVIEW_HOME ?? join(root, '.test-runtime/preview'));
@@ -23,7 +25,7 @@ const artifacts = join(root, '.artifacts');
// pnpm includes local archive paths in its store index filename. Keep the
// immutable archive path short even when this checkout is a nested worktree.
const previewPacks = resolve(process.env.WORKDSH_PREVIEW_PACKS_HOME ?? join(homedir(), '.cache/workdsh-preview-packs'));
-const env = { ...process.env, DSH_HOME: home, PATH: `${join(root, 'node_modules/.bin')}:${dirname(process.execPath)}:${process.env.PATH}` };
+const env = { ...process.env, DSH_HOME: home, DSH_AGENTS_HOME: process.env.DSH_AGENTS_HOME ?? join(home, 'agents'), PATH: `${join(root, 'node_modules/.bin')}:${dirname(process.execPath)}:${process.env.PATH}` };
const exec = promisify(execFile);
const run = async (tool, args) => {
await exec(process.execPath, [join(root, 'node_modules', tool), ...args], { cwd: root, env, timeout: 600_000, maxBuffer: 8 * 1024 * 1024 });
@@ -68,9 +70,10 @@ if (normalizedManifest) {
const currentDependencies = JSON.parse(await readFile(previewManifestPath, 'utf8')).dependencies ?? {};
const layersMatch = currentDependencies['@deepseek-ai/dsh-base'] === baseVersion
&& currentDependencies['@deepseek-ai/dsh-web-app'] === webAppVersion
+ && currentDependencies.dshmarket === marketVersion
&& packages.every(({ manifest }, index) => currentDependencies[manifest.name] === `file:${tarballs[index]}`);
if (!layersMatch) {
- await run('@deepseek-ai/dsh/lib/bin.js', ['plugin', '--profile', 'preview', 'add', baseSpec, webAppSpec, ...tarballs]);
+ await run('@deepseek-ai/dsh/lib/bin.js', ['plugin', '--profile', 'preview', 'add', baseSpec, webAppSpec, marketSpec, ...tarballs]);
}
// Boot and ConfigEditor share module-local registration in dsh-app-boot.
// Keep the official CLI in the Profile dependency graph as well: launching the
diff --git a/workdsh-web/scripts/pack-project-release.mjs b/workdsh-web/scripts/pack-project-release.mjs
index fb76763b42..0852ce13e4 100644
--- a/workdsh-web/scripts/pack-project-release.mjs
+++ b/workdsh-web/scripts/pack-project-release.mjs
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'node:url';
const root = resolve(fileURLToPath(new URL('..', import.meta.url)));
const project = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
const tag = `v${project.version}`;
-const releaseNotes = join(root, 'docs', 'releases', `${tag}.md`);
+const releaseNotes = join(root, 'RELEASE-NOTES.md');
const destination = join(root, '.artifacts', `project-${tag}`);
const packageDirectories = [
'packages/providers/identity-local',
@@ -28,13 +28,23 @@ await rm(destination, { recursive: true, force: true });
await mkdir(destination, { recursive: true });
for (const directory of packageDirectories) {
- execFileSync('corepack', ['pnpm', 'pack', '--pack-destination', destination], {
+ execFileSync(process.platform === 'win32' ? 'corepack.cmd' : 'corepack', ['pnpm', 'pack', '--pack-destination', destination], {
cwd: join(root, directory),
+ shell: process.platform === 'win32',
stdio: 'inherit',
});
}
const sourceCommit = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' }).trim();
+let sourceDirty = false;
+try {
+ // Desktop package checks regenerate a tracked Windows icon. Web release
+ // cleanliness covers the Web source and the DSH version inputs it consumes.
+ execFileSync('git', ['diff', '--quiet', '--ignore-submodules=dirty', 'HEAD', '--', 'workdsh-web', 'upstream.json', 'deepseek-harness'], { cwd: root, stdio: 'ignore' });
+} catch (error) {
+ if (error.status !== 1) throw error;
+ sourceDirty = true;
+}
const packages = [];
for (const directory of packageDirectories) {
const manifest = JSON.parse(await readFile(join(root, directory, 'package.json'), 'utf8'));
@@ -57,18 +67,18 @@ await writeFile(join(destination, 'release-manifest.json'), JSON.stringify({
tag,
channel: 'github-release',
sourceCommit,
- sourceDirty: execFileSync('git', ['status', '--porcelain'], { cwd: root, encoding: 'utf8' }).trim().length > 0,
+ sourceDirty,
harness: '0.1.7-rc.2',
node: process.version,
packageManager: project.packageManager,
runtimeOverrides: Object.fromEntries(Object.entries(project.pnpm.overrides).filter(([name]) => name.startsWith('@deepseek-ai/'))),
packages,
- verified: ['Package checksums generated; see docs/DSH-0.1.7-UPGRADE-PLAN.md for runtime validation evidence'],
+ verified: ['Package checksums generated; CI validates build, types, DSH version alignment and integration tests'],
limitations: [
'alpha preview; package APIs and stored data may change',
'interactive OAuth, connector multi-account switching and public authorization are not complete',
'hour-scale expert-team soak, official fork-member browser history, arbitrary Office fidelity and cross-platform acceptance remain incomplete',
- 'relative to alpha.6, the packaged expert-team long task, real-model two-stage handoff, connector isolation probe and Tencent Docs connection were not re-run on this batch artifacts',
+ 'real-model end-to-end workflows and every third-party Skill or plugin remain outside automated release checks',
'packages are GitHub assets and are not published to the npm registry',
],
}, null, 2) + '\n');
diff --git a/workdsh-web/scripts/start-preview.mjs b/workdsh-web/scripts/start-preview.mjs
index 2b17d431b2..753be38907 100644
--- a/workdsh-web/scripts/start-preview.mjs
+++ b/workdsh-web/scripts/start-preview.mjs
@@ -1,14 +1,13 @@
import { spawn } from 'node:child_process';
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
import { createRequire } from 'node:module';
-import { homedir } from 'node:os';
-import { dirname, resolve } from 'node:path';
+import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { withoutRedundantAgentTeamProfile } from './preview-agent-team.mjs';
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const previewHome = process.env.WORKDSH_PREVIEW_HOME ?? resolve(root, '.test-runtime/preview');
-const agentsHome = process.env.DSH_AGENTS_HOME ?? resolve(homedir(), '.agents');
+const agentsHome = process.env.DSH_AGENTS_HOME ?? join(previewHome, 'agents');
const port = process.env.WORKDSH_PREVIEW_PORT ?? '18989';
// Preview currently needs a larger startup heap; this does not fix the underlying growth.
const heapMb = process.env.WORKDSH_PREVIEW_HEAP_MB ?? '8192';
diff --git a/workdsh-web/tests/integration/skill-manager.test.mjs b/workdsh-web/tests/integration/skill-manager.test.mjs
index 0f44152845..2592889072 100644
--- a/workdsh-web/tests/integration/skill-manager.test.mjs
+++ b/workdsh-web/tests/integration/skill-manager.test.mjs
@@ -12,6 +12,92 @@ import { SkillManager } from '../../packages/plugins/skills/dist/index.js';
const skillPackageRequire = createRequire(new URL('../../packages/plugins/skills/package.json', import.meta.url));
const { zipSync } = skillPackageRequire('fflate');
+test('default managed skills stay inside the WorkDSH home', async () => {
+ const root = await mkdtemp(join(tmpdir(), 'workdsh-skill-private-home-'));
+ const dshHome = join(root, 'dsh');
+ const agentsHome = join(dshHome, 'agents');
+ const originalAgentsHome = process.env.DSH_AGENTS_HOME;
+ const originalDshHome = process.env.DSH_HOME;
+ const ctx = new Context();
+ try {
+ process.env.DSH_HOME = dshHome;
+ delete process.env.DSH_AGENTS_HOME;
+ await mkdir(join(agentsHome, 'skills', 'private-skill'), { recursive: true });
+ await writeFile(join(agentsHome, 'skills', 'private-skill', 'SKILL.md'), '---\nname: private-skill\ndescription: Private WorkDSH skill\n---\n');
+ await ctx.plugin(SkillRegistry);
+ await ctx.plugin(filesystem, { dshHome, agentsHome, watch: false });
+ new SkillManager(ctx);
+ const skills = await ctx.workdshSkills.list();
+ assert.equal(skills.find(skill => skill.name === 'private-skill')?.manageable, true);
+ assert.equal((await readdir(join(root))).includes('.agents'), false);
+ } finally {
+ await ctx.fiber.dispose();
+ if (originalAgentsHome === undefined) delete process.env.DSH_AGENTS_HOME; else process.env.DSH_AGENTS_HOME = originalAgentsHome;
+ if (originalDshHome === undefined) delete process.env.DSH_HOME; else process.env.DSH_HOME = originalDshHome;
+ await rm(root, { recursive: true, force: true });
+ }
+});
+
+test('SkillHub directory slug may differ from the official DSH skill name', async () => {
+ const root = await mkdtemp(join(tmpdir(), 'workdsh-skillhub-name-'));
+ const dshHome = join(root, 'dsh'); const agentsHome = join(dshHome, 'agents');
+ const originalDshHome = process.env.DSH_HOME; const originalAgentsHome = process.env.DSH_AGENTS_HOME;
+ const file = join(dshHome, 'skills', 'marketplace-slug', 'SKILL.md');
+ const document = '---\nname: original-name\ndescription: Marketplace skill\n---\nInstructions\n';
+ const ctx = new Context();
+ try {
+ process.env.DSH_HOME = dshHome; delete process.env.DSH_AGENTS_HOME;
+ await mkdir(join(dshHome, 'skills', 'marketplace-slug'), { recursive: true });
+ await writeFile(file, document);
+ await ctx.plugin(SkillRegistry);
+ await ctx.plugin(filesystem, { dshHome, agentsHome, watch: false });
+ new SkillManager(ctx);
+ const rows = await ctx.workdshSkills.list();
+ assert.equal(rows.some(row => row.name === 'marketplace-slug'), false);
+ assert.equal(rows.find(row => row.name === 'original-name')?.state, 'enabled');
+ assert.equal((await ctx.workdshSkills.detail('original-name'))?.manageable, true);
+ assert.equal(await readFile(file, 'utf8'), document);
+ await ctx.workdshSkills.setEnabled('original-name', false);
+ assert.equal((await ctx.workdshSkills.list()).find(row => row.name === 'original-name')?.state, 'disabled');
+ await ctx.workdshSkills.setEnabled('original-name', true);
+ assert.equal((await ctx.workdshSkills.list()).find(row => row.name === 'original-name')?.state, 'enabled');
+ assert.equal(await readFile(file, 'utf8'), document);
+ } finally {
+ await ctx.fiber.dispose();
+ if (originalDshHome === undefined) delete process.env.DSH_HOME; else process.env.DSH_HOME = originalDshHome;
+ if (originalAgentsHome === undefined) delete process.env.DSH_AGENTS_HOME; else process.env.DSH_AGENTS_HOME = originalAgentsHome;
+ await rm(root, { recursive: true, force: true });
+ }
+});
+
+test('Web Profile lists SkillHub skills by declared name without a root filesystem provider', async () => {
+ const root = await mkdtemp(join(tmpdir(), 'workdsh-skillhub-web-name-'));
+ const dshHome = join(root, 'dsh');
+ const originalDshHome = process.env.DSH_HOME; const originalAgentsHome = process.env.DSH_AGENTS_HOME;
+ const file = join(dshHome, 'skills', 'marketplace-slug', 'SKILL.md');
+ const ctx = new Context();
+ try {
+ process.env.DSH_HOME = dshHome; delete process.env.DSH_AGENTS_HOME;
+ await mkdir(join(dshHome, 'skills', 'marketplace-slug'), { recursive: true });
+ await writeFile(file, '---\nname: declared-name\ndescription: SkillHub example\n---\nInstructions\n');
+ await ctx.plugin(SkillRegistry);
+ new SkillManager(ctx);
+ const rows = await ctx.workdshSkills.list();
+ assert.equal(rows.some(row => row.name === 'marketplace-slug'), false);
+ assert.equal(rows.find(row => row.name === 'declared-name')?.state, 'enabled');
+ assert.equal((await ctx.workdshSkills.detail('declared-name'))?.manageable, true);
+ await ctx.workdshSkills.setEnabled('declared-name', false);
+ assert.equal((await ctx.workdshSkills.list()).find(row => row.name === 'declared-name')?.state, 'disabled');
+ await ctx.workdshSkills.setEnabled('declared-name', true);
+ assert.equal((await ctx.workdshSkills.list()).find(row => row.name === 'declared-name')?.state, 'enabled');
+ } finally {
+ await ctx.fiber.dispose();
+ if (originalDshHome === undefined) delete process.env.DSH_HOME; else process.env.DSH_HOME = originalDshHome;
+ if (originalAgentsHome === undefined) delete process.env.DSH_AGENTS_HOME; else process.env.DSH_AGENTS_HOME = originalAgentsHome;
+ await rm(root, { recursive: true, force: true });
+ }
+});
+
test('canonical skill paths remain manageable through a home alias without selecting a shadowed local copy', async () => {
const root = await mkdtemp(join(tmpdir(), 'workdsh-skill-canonical-'));
const physicalHome = join(root, 'physical-agents');
@@ -128,10 +214,10 @@ test('skill manager surfaces invalid local skills with actionable diagnostics',
const summary = (await ctx.workdshSkills.list()).find(row => row.name === 'broken-skill');
assert.equal(summary.state, 'invalid');
assert.equal(summary.modelInvocable, false);
- assert.deepEqual(summary.diagnostics.map(item => item.code), ['name-mismatch', 'description-required', 'instructions-required']);
+ assert.deepEqual(summary.diagnostics.map(item => item.code), ['description-required']);
const detail = await ctx.workdshSkills.detail('broken-skill');
assert.equal(detail.state, 'invalid');
- assert.match(detail.diagnostics[0].message, /技能目录/);
+ assert.match(detail.diagnostics[0].message, /description/);
const fixed = '---\nname: broken-skill\ndescription: Repaired skill\n---\nUse these repaired instructions.\n';
const repaired = await ctx.workdshSkills.update({ name: 'broken-skill', document: fixed, expectedRevision: detail.revision });
assert.equal(repaired.state, 'enabled');