diff --git a/README.es.md b/README.es.md index aab55f0b1..f0f14542a 100644 --- a/README.es.md +++ b/README.es.md @@ -25,7 +25,7 @@ DSH Desktop convierte la experiencia local de DeepSeek Harness en una aplicación de escritorio instalable. Inicia Harness automáticamente, guarda Profile, plugins, espacios de trabajo, ajustes de modelos y sesiones fuera del directorio de la aplicación y abre la interfaz completa cuando el Runtime local está listo. > [!IMPORTANT] -> DSH Desktop es una versión preliminar basada en `@deepseek-ai/dsh@0.1.1-rc.2`, que evoluciona rápidamente. Las versiones de macOS están firmadas y notarizadas por Apple. Los instaladores para Windows x64 también están firmados; las advertencias de seguridad de Windows pueden disminuir gradualmente a medida que el editor acumula reputación de descargas e instalaciones. +> DSH Desktop es una versión preliminar basada en `@deepseek-ai/dsh@0.1.2-alpha.4`, que evoluciona rápidamente. Las versiones de macOS están firmadas y notarizadas por Apple. Los instaladores para Windows x64 también están firmados; las advertencias de seguridad de Windows pueden disminuir gradualmente a medida que el editor acumula reputación de descargas e instalaciones. ## Descarga diff --git a/README.ja.md b/README.ja.md index 6db9f8594..ff49794e0 100644 --- a/README.ja.md +++ b/README.ja.md @@ -24,7 +24,7 @@ DSH Desktop は、ローカルの DeepSeek Harness をインストール可能なデスクトップアプリとして提供します。Harness を自動起動し、Profile、プラグイン、ワークスペース、モデル設定、セッションをアプリ本体とは別の場所に保存し、ローカル Runtime の準備が整うと完全な Harness 画面を開きます。 > [!IMPORTANT] -> DSH Desktop は、急速に進化している `@deepseek-ai/dsh@0.1.1-rc.2` を基盤とする早期プレビューです。macOS 版はコード署名と Apple 公証済みです。Windows x64 インストーラーもコード署名済みですが、発行元のダウンロード・インストール実績が蓄積されるまでは Windows のセキュリティ警告が表示される場合があります。 +> DSH Desktop は、急速に進化している `@deepseek-ai/dsh@0.1.2-alpha.4` を基盤とする早期プレビューです。macOS 版はコード署名と Apple 公証済みです。Windows x64 インストーラーもコード署名済みですが、発行元のダウンロード・インストール実績が蓄積されるまでは Windows のセキュリティ警告が表示される場合があります。 ## ダウンロード diff --git a/README.md b/README.md index 87c8a7d16..24d940efa 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ DSH Desktop packages the local DeepSeek Harness experience as an installed desktop application. It starts Harness automatically, keeps profiles, plugins, workspaces, model settings, and sessions outside the application directory, and opens the full Harness interface as soon as the local runtime is ready. > [!IMPORTANT] -> DSH Desktop is an early preview built on the rapidly evolving `@deepseek-ai/dsh@0.1.1-rc.2`. macOS releases are code-signed and notarized by Apple. Windows x64 installers are code-signed; Windows security warnings may still decrease gradually as the publisher builds download and installation reputation. +> DSH Desktop is an early preview built on the rapidly evolving `@deepseek-ai/dsh@0.1.2-alpha.4`. macOS releases are code-signed and notarized by Apple. Windows x64 installers are code-signed; Windows security warnings may still decrease gradually as the publisher builds download and installation reputation. ## Download diff --git a/README.pt.md b/README.pt.md index f03a45b6a..8c5e5797f 100644 --- a/README.pt.md +++ b/README.pt.md @@ -25,7 +25,7 @@ O DSH Desktop transforma a experiência local do DeepSeek Harness em um aplicativo desktop instalável. Ele inicia o Harness automaticamente, armazena Profile, plugins, espaços de trabalho, configurações de modelos e sessões fora do diretório do aplicativo e abre a interface completa quando o Runtime local está pronto. > [!IMPORTANT] -> O DSH Desktop é uma versão inicial baseada no `@deepseek-ai/dsh@0.1.1-rc.2`, que evolui rapidamente. As versões para macOS são assinadas e notarizadas pela Apple. Os instaladores para Windows x64 também são assinados; os avisos de segurança do Windows podem diminuir gradualmente à medida que o editor acumula reputação de downloads e instalações. +> O DSH Desktop é uma versão inicial baseada no `@deepseek-ai/dsh@0.1.2-alpha.4`, que evolui rapidamente. As versões para macOS são assinadas e notarizadas pela Apple. Os instaladores para Windows x64 também são assinados; os avisos de segurança do Windows podem diminuir gradualmente à medida que o editor acumula reputação de downloads e instalações. ## Download diff --git a/README.ru.md b/README.ru.md index 9897b70d7..b8952eb31 100644 --- a/README.ru.md +++ b/README.ru.md @@ -25,7 +25,7 @@ DSH Desktop превращает локальный DeepSeek Harness в устанавливаемое настольное приложение. Оно автоматически запускает Harness, хранит Profile, плагины, рабочие пространства, настройки моделей и сессии вне каталога приложения и открывает полный интерфейс Harness, когда локальный Runtime готов. > [!IMPORTANT] -> DSH Desktop — ранняя предварительная версия на основе быстро развивающегося `@deepseek-ai/dsh@0.1.1-rc.2`. Выпуски для macOS подписаны и нотариально заверены Apple. Установщики Windows x64 также подписаны; предупреждения безопасности Windows могут уменьшаться постепенно по мере накопления репутации загрузок и установок. +> DSH Desktop — ранняя предварительная версия на основе быстро развивающегося `@deepseek-ai/dsh@0.1.2-alpha.4`. Выпуски для macOS подписаны и нотариально заверены Apple. Установщики Windows x64 также подписаны; предупреждения безопасности Windows могут уменьшаться постепенно по мере накопления репутации загрузок и установок. ## Загрузка diff --git a/README.zh.md b/README.zh.md index 7d7bb3a0a..d04bac4d9 100644 --- a/README.zh.md +++ b/README.zh.md @@ -24,7 +24,7 @@ DSH Desktop 把本地 DeepSeek Harness 封装为可安装的桌面应用。它会自动启动 Harness,把 Profile、插件、工作区、模型配置和会话保存在应用安装目录之外,并在本地 Runtime 就绪后直接进入完整 Harness 界面。 > [!IMPORTANT] -> DSH Desktop 当前处于早期预览阶段,基于仍在快速迭代的 `@deepseek-ai/dsh@0.1.1-rc.2`。macOS 正式包已完成代码签名并通过 Apple 公证;Windows x64 安装包也已完成代码签名。随着下载量、安装量和发行者信誉逐步积累,Windows 安全提示会逐渐减少,但不会立即消失。 +> DSH Desktop 当前处于早期预览阶段,基于仍在快速迭代的 `@deepseek-ai/dsh@0.1.2-alpha.4`。macOS 正式包已完成代码签名并通过 Apple 公证;Windows x64 安装包也已完成代码签名。随着下载量、安装量和发行者信誉逐步积累,Windows 安全提示会逐渐减少,但不会立即消失。 ## 下载安装 diff --git a/docs/harness-0.1.2-alpha.3-upgrade.md b/docs/harness-0.1.2-alpha.3-upgrade.md new file mode 100644 index 000000000..7b0aa7551 --- /dev/null +++ b/docs/harness-0.1.2-alpha.3-upgrade.md @@ -0,0 +1,211 @@ +# 升级到 Harness 0.1.2-alpha.3 + +对照上游 [`dsh-v0.1.2-alpha.3`](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.2-alpha.3) +(commit `dd6322d604e00eec1ba5e0c8541159906a21094a`)从 `0.1.2-alpha.1` 升级。 +上游的 alpha.2 未在本仓落地,本次直接对比 alpha.1 → alpha.3。 + +前一段升级(`0.1.1-rc.2` → `0.1.2-alpha.1`)见 +[harness-0.1.2-upgrade.md](./harness-0.1.2-upgrade.md)。 + +## 状态 + +升级已完成并验证: + +``` +npm ci(含 postinstall) → 0;16 个补丁全部 ✔ +npm run build → ok +npx vitest run → 75 files / 560 tests 全绿,exit 0 +tsc --noEmit(typecheck) → 0 错误 +verify-harness-auth.mjs → 捕获令牌 → 401(无 cookie) → 303(令牌换 cookie) → 200(带 cookie) 全通过 +实际启动 Electron → 正常运行;dev harness web (http://127.0.0.1:43128/) 返回 200; + profiles/node_modules 4 个桌面插件(client-ui / preset-transfer / + hmr-fallback / market-installer)全部镜像挂载;无 plugin-recovery UI +``` + +**唯一未完成的一步**(同 alpha.1):上游尚未发布到 npm,依赖暂时指向仓库内本地打包的 tarball。 +包发布后需要把 `package.json` 换回语义化版本并重新生成 lockfile。 + +> 验证时务必用完整的 `npm ci`。带 `--ignore-scripts` 会跳过 `postinstall`, +> 而 `postinstall` 既装 Electron 二进制也改前端品牌资源——跳过它,测试和 typecheck +> 依然全绿,应用却根本起不来。 + +## 本地打包(上游未发布的临时桥接) + +上游自带发布流水线,无需等 registry: + +```bash +git clone --depth 1 --branch dsh-v0.1.2-alpha.3 \ + https://github.com/deepseek-ai/deepseek-harness.git +cd deepseek-harness +corepack enable # packageManager 指定 pnpm@11.7.0 +corepack pnpm install --frozen-lockfile +corepack pnpm run build:official # release:pack 要求 official 客户端构建记录 +corepack pnpm exec tsx scripts/release/pack.ts --family vendor --out dist/npm-vendor --concurrency 8 +corepack pnpm exec tsx scripts/release/pack.ts --family dsh --out dist/npm-dsh --concurrency 8 +``` + +`build:official` 记录 220 个 client artifact / 4 个 public value。产出 +**9 个 vendor + 244 个 dsh** tarball(alpha.1 为 9 + 241),已提交到 +[`packages/harness-0.1.2-alpha.3/`](../packages/harness-0.1.2-alpha.3/)(含各 family 的 +`publish-order.txt`)。旧目录 `packages/harness-0.1.2-alpha.1/` 已在同一提交删除。 +`package.json` 以仓库相对 `file:` 路径引用这些 tarball,所以 `npm ci` 对所有人可复现。 + +注意 `pnpm run release:pack -- --family dsh` 会把参数当位置参数报错,需按上面直接 `pnpm exec tsx` 调用。 + +**注意补丁里的绝对路径 context 行**:`dsh-client-ui-{agent-preset,model-selection,settings-models,workspace}` 四个补丁(共 5 处 hunk)里有一行 `//#region \0dsh-css:<绝对路径>...module.css.mjs`——这是上游构建产物内嵌的、打包机器上的源码绝对路径,作为 context 行被 `patch-package` 逐行匹配。**用仓库里已提交的 tarball 跑 `npm ci` 对所有人可复现**;但如果换一台机器重新 clone 上游 `pack`,这个路径会变,届时这 4 个补丁需要对着新产物重新生成(其余 12 个补丁不受影响)。 + +## 上游结构性变更(alpha.1 → alpha.3) + +### 被删除的包 + +| 包名 | 处置 | +| --- | --- | +| `@deepseek-ai/dsh-agent-spine-demo`(`packages/examples` 整组删除) | 上游把其配置行内联进 `dsh-sdk-minimal`。桌面端只是 `package.json` 一行依赖,直接移除;`@deepseek-ai/dsh` 运行时闭包与 `patches/@deepseek-ai+dsh` 均未引用它。 | +| `@deepseek-ai/dsh-session-persistence-sqlite` | 可选的**权威存储后端**(JSONL 的替代),整包删除。桌面端从未打包(一直用 `dsh-session-persistence-jsonl`)。`test/release.test.ts` 的 `excludedHarnessPackages` 里针对它的排除行已过时,删除并留注释。 | + +`dsh-storage-sqlite` 的排除行保留为防御性守卫(现已 vacuous,加注释说明现状)。 + +### 新增的包(5 个,全部提升为 `file:` 直接依赖) + +`@deepseek-ai/dsh-client-ui-schedule`、`@deepseek-ai/dsh-deque`、 +`@deepseek-ai/dsh-session-turn-outline`、`@deepseek-ai/dsh-util-time`、 +`@deepseek-ai/dsh-util-values`。 + +这 5 个都是已交付包的传递依赖(`npm ls` + `package.json` 反查确认,无孤儿包), +按字母序加入 `package.json` 的 `dependencies` 作 `file:` 直接依赖。否则 lockfile +会从 registry(npmmirror 镜像)解析它们,破坏「仓库内 tarball」约束。 +`test/release.test.ts` 新增用例校验这 5 包为 `file:` 依赖且 lockfile 无 registry 解析的 +`@deepseek-ai/dsh*`。 + +> brief 曾预测新包名为 `dsh-util-deque`,实际 tarball 名是 `@deepseek-ai/dsh-deque`; +> `dsh-session-turn-outline` 未被 brief 预测到。均以实际打包产物为准。 + +### vendor 版本号全部上抬 + +9 个 vendor 包名不变,版本号全部 patch/minor 上抬:cordis `4.0.1→4.0.2`、 +cordis-plugin-group `1.0.1→1.0.2`、cordis-plugin-hmr `1.0.16→1.0.17`、 +cordis-plugin-include `1.0.6→1.0.7`、cordis-plugin-loader `1.0.2→1.0.3`、 +cordis-plugin-logger-console `1.0.1→1.0.2`、cordis-plugin-timer `1.1.3→1.1.4`、 +cosmokit `1.8.2→1.8.3`、schemastery `3.18.1→3.18.2`。 +`package.json` 的 `file:` 行按实际文件名逐个替换(不只是路径段)。 + +### 未受影响 + +`CORE_BUNDLES`(`@deepseek-ai/dsh-base` / `@deepseek-ai/dsh-web-app` / `dshmarket`) +名称路径不变,`harness-runtime.ts` 无需改动。 + +## 会话持久化(alpha.3 breaking change) + +上游 `refactor(session)!: remove SQLite persistence backend`: + +- 删除的是**权威存储后端** `session-persistence-sqlite`(JSONL 的可选替代品)。 +- 上游保留 `session-query-sqlite`(可丢弃的 FTS5 搜索索引,从 JSONL 重建)与 + `dsh-storage-sqlite`(通用 domain-KV)——桌面端交付闭包实际两者都不含。 +- 桌面端 profile 用 `dsh-session-persistence-jsonl` 作为唯一权威存储, + 历史会话可见性完全不受影响。 +- 上游那句「请用旧版本导出」仅针对手动把后端配成 sqlite 的上游 CLI 用户,与桌面端无关。 + +桌面端原有的“永久删除会话、保留工作区文件”补丁也已迁移到 alpha.3 的新会话架构: +删除 RPC 现在由 `dsh-api-session-controller` 统一承载,经 +`dsh-session-persistence` / `dsh-session-persistence-jsonl` 删除权威 JSONL 记录,再由 +`dsh-workspace` 清理会话索引。UI 在删除前明确提示工作区文件会保留且操作不可撤销;普通 +会话的活动 Agent 会由 controller 自己释放,子 Agent 或其他 capability 持有的会话仍拒绝删除。 + +## 主机 API / 移动端桥接(复核结论:无契约变化) + +逐一比对 alpha.1 → alpha.3 的 `dsh-web-app` / `dsh-client-connection` / +`host/webserver` / `api/*`: + +- `dsh web:` 令牌打印行(`web-app/src/index.ts`)——逐字不变。 +- headless / 浏览器打不开时的诊断仍写 stderr——就绪探测与错误归类不受影响。 +- `CORE_BUNDLES`、主窗口 `/?token=` 首次导航、令牌换签名 cookie 流程——不变。 +- 桥接端点全集(`session/list|create|prompt|cancel|selectModel|modelCatalog|page|follow`、 + `agentPresets/list|select`、`workspace/follow`)以 `connection` 的 dispatch switch + 逐行比对——一致。`session/modelCatalog` 仍无参、非按会话;`session/page` 仍游标分页。 +- `/api/remote.mux` 线协议(`gateway/src/stream-protocol.ts`、`remote-events.ts`)—— + 两 tag 间**零 diff**。`$events` / `$events/result` / `workspace/follow` 订阅不变。 +- `dsh-web-app --trusted-host`:桌面固定 `--host 127.0.0.1` 不派生 LAN 地址,桥接以 + loopback 身份回源,依旧无关。 + +**唯一变化**:RPC 错误码普遍加 `gateway/` 命名空间前缀(`bad-request` → +`gateway/bad-request` 等)。桌面运行时与桥接只取 `error.message`(字符串), +不消费错误码,`src/` 无对 Harness RPC 错误码的字符串匹配——无影响。 + +`src/main/**` 一行未改,测试未改。 + +## 补丁清单(16 → 19) + +全部 19 个补丁已适配 alpha.3,无 alpha.1 残留;`npm ci` 的 patch-package 0 error。 + +### 仅改名 / 行号漂移 / blob-index 偏移(内容逐字不变) + +| 补丁 | 说明 | +| --- | --- | +| `cordis-plugin-loader`(`1.0.2` → `1.0.3`) | 文件名版本号也变,补丁体不变 | +| `@deepseek-ai+dsh`(依赖闭包 manifest 注入) | 纯 rename + blob index 更新,`@@` hunk 头未变 | +| `dsh-client-modules`、`dsh-client-ui-deliverables`、`dsh-client-ui-directory-picker-native`、`dsh-client-ui-layout`、`dsh-client-ui-sidebar`、`dsh-llm-deepseek`、`dsh-llm-pi-ai` | 纯改名,补丁体 0 改动(上下文未漂移) | +| `dsh-client-ui-chat`、`dsh-client-ui-trajectory` | 仅行号漂移,provider 错误分类逐字不变(alpha.3 pristine 仍只处理 AUTH) | + +`test/session-create-remote-event-patch.test.ts` 的 `isJsonValue` import 从 +`@deepseek-ai/dsh-session` 迁到 `@deepseek-ai/dsh-util-values`(上游把该导出移出包入口)。 + +### CSS module hash 迁移 + +| 补丁 | 旧 hash → 新 hash | 附带 | +| --- | --- | --- | +| `dsh-client-ui-model-selection` | `_2WBGbq_` → `Ydf5zq_` | 13 hunk 保持;纯机械 hash swap | +| `dsh-client-ui-workspace` | `koIWyW_` → `_zvJpq_` | 保留原有未读 / Finder 能力,并接入永久删除确认与错误展示;上游 workspace 行新增 schedule indicator 功能 | +| `dsh-client-ui-settings-models` | `uVX9wq_` → `A_4Mua_`(ModelsSection)、`oGvYtW_` → `_2CJT8a_`(Onboarding) | 顺带修掉 alpha.1 补丁自身的 dual-hash bug(手写 CSS 串混用 stale `zGbnIq_`);`test/model-settings-catalog-ux-patch.test.ts` 1 行断言更新 | +| `dsh-client-ui-agent-preset` | Seat `_4FiJda_` → `YgMYBq_` | 顺带修正 alpha.1 误把 picker/search 类挂到 `AgentPresetLabel` 模块(Seat 组件读的是 `AgentPresetSeat` 模块,alpha.1 那份样式大概率静默失效);hunk 19 → 18 | + +### 交还给上游的能力(补丁里删掉的部分) + +- **`dsh-client-ui-settings-models`**:`EditorFooter` 的 `submitLabelKey*` / + `submitBusyLabelKey*` prop 上游 alpha.3 已原生提供,补丁只留 sticky-footer 包裹的 + context 行;onboarding 组件的 `api` prop 上游重命名为 `operations`,补丁跟随。 +- **`dsh-client-ui-agent-preset`**:`builtInGroup` / `customGroup` 两个 i18n 键上游 + alpha.3 已原生(「内置 / 自定义」),补丁删掉重复键、改为直接复用上游键。 + +### 仍归桌面端(alpha.3 上游无原生等价,全部保留 rebase) + +- provider 错误分类(QUOTA / FORBIDDEN,chat + trajectory,中英 i18n) +- `dsh-api-session-controller` 的 JSON-safe projection 剥离 +- `dsh-client-ui-workspace` 的未读会话标记(`unreadSessionIds`) +- 永久删除会话记录与执行轨迹(保留工作区文件;`dsh-api-session-controller`、JSONL + persistence、workspace registry 与 `dsh-client-ui-workspace` 协同) +- 可搜索模型 / 服务商网格(`dsh-client-ui-model-selection` + `dsh-client-ui-settings-models`) +- 预设导入导出 UI + 可搜索/分组列表 + Awesome Presets 链接(`dsh-client-ui-agent-preset`) + +## 其他需要注意的 + +- `packages/dsh-desktop-{client-ui,preset-transfer,market-installer}` 三个桌面插件包的 + `@deepseek-ai/dsh*` peer 依赖现均为 `^0.1.2-alpha.3`(`market-installer` 在依赖切换提交 + `ade05bc` 中一并更新,另两个随版本字样清理提交 `454d0a9` 更新)——预发布版的 caret 只在 + 同一 `major.minor.patch` 内匹配,所以每次上游发版这些 token 都要跟着对齐。注意从 alpha.1 + 上调到 alpha.3 是抬高下限(收紧),并非放宽;`^0.1.2-alpha.1` 本就能匹配 `0.1.2-alpha.3`, + 这里只是版本字样对齐,不是功能性放宽。 +- `packages/dsh-desktop-preset-transfer/index.js` 的 `PRESET_SOURCE_DSH_VERSION` + → `0.1.2-alpha.3`(连带 `test/preset-transfer-patch.test.ts` / `test/profile-compatibility.test.ts` + 的 fixture 与断言)。 +- lockfile 重生成后体积增长(+1605 行)——npmmirror 镜像给第三方传递依赖回填了 + `resolved` / `integrity`。`@deepseek-ai/dsh*` 仍全部走 `file:` tarball,无 registry + 回退,无 alpha.1 残留,非回归。 +- `@deepseek-ai/node-addon-landlock-run-linux-{arm64,x64}` 仍从 npmmirror 解析—— + 这是 landlock native addon 的平台包,升级前即如此,非 `dsh-*` 业务包,离线环境需注意。 +- 6 份 README(en / zh / ja / es / pt / ru)第 27~28 行的 `@deepseek-ai/dsh@0.1.1-rc.2` + 字样更新为 `@deepseek-ai/dsh@0.1.2-alpha.3`(连带 `test/readme-parity.test.ts` 的 + `requiredFacts` token)。 +- `src/main/**` 与 `scripts/**` 里的 "Since 0.1.2-alpha.1" 注释是**机制引入版本**的准确 + 记述(令牌认证、Gateway stream carrier 确实是 alpha.1 引入),按规则保留不改。 +- `install-brand-assets.mjs` 在 alpha.3 前端资源上验证通过,无需改动 + (icon link → `/dsh-desktop-logo.png`,manifest `purpose:"any"` 保留)。 + +## 剩余工作 + +上游发布到 npm 后: + +1. 将 `package.json` 中保留的运行时闭包 `file:` 依赖换回 `0.1.2-alpha.3` 语义化版本; + 不要重新加入未启用的 provider 和测试包 +2. 删除 `packages/harness-0.1.2-alpha.3/` +3. 重新生成 `package-lock.json`(必须来自真实 registry) +4. 重跑 `npm ci` + `npx vitest run` + `scripts/verify-harness-auth.mjs` diff --git a/docs/harness-0.1.2-alpha.4-upgrade.md b/docs/harness-0.1.2-alpha.4-upgrade.md new file mode 100644 index 000000000..91cc6850a --- /dev/null +++ b/docs/harness-0.1.2-alpha.4-upgrade.md @@ -0,0 +1,83 @@ +# 升级到 Harness 0.1.2-alpha.4 + +对照上游 [`dsh-v0.1.2-alpha.4`](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.2-alpha.4) +(commit `ad05e08b11c97a8ec58d20367eb70b86a070624d`)从 `0.1.2-alpha.3` 升级。 + +前两段升级见: +- `0.1.1-rc.2` → `0.1.2-alpha.1`:[harness-0.1.2-upgrade.md](./harness-0.1.2-upgrade.md) +- `0.1.2-alpha.1` → `0.1.2-alpha.3`:[harness-0.1.2-alpha.3-upgrade.md](./harness-0.1.2-alpha.3-upgrade.md) + +## 状态 + +升级已完成并验证: + +``` +npm ci(含 postinstall) → 0;19 个补丁全部 clean apply (0 warn, 0 error) +npm run build → ok (628kB main / 31kB preload) +npx vitest run → 76 files / 569 tests 全绿,exit 0 +tsc --noEmit(typecheck) → 0 错误 +scripts/verify-harness-auth.mjs → 捕获令牌 → 401(无 cookie) → 303(令牌换 cookie) → 200(带 cookie) 全通过 +会话分页冒烟验证 (session/page) → 创建会话 + session/page throughSeq: -1 正常响应 200,records / hasMore 结构完整 +实际启动 Electron → 正常启动;profiles/node_modules 桌面插件全部挂载;无 plugin-recovery UI +``` + +**唯一未完成的一步**(同前序 alpha 版本):上游尚未发布到 npm registry,依赖指向仓库内本地打包的 tarball。上游正式发布后需要把 `package.json` 换回语义化版本并重新生成 lockfile。 + +## 本地打包 + +上游打包命令: + +```bash +git clone --depth 1 --branch dsh-v0.1.2-alpha.4 \ + https://github.com/deepseek-ai/deepseek-harness.git +cd deepseek-harness +corepack enable # packageManager 指定 pnpm@11.7.0 +corepack pnpm install --frozen-lockfile +corepack pnpm run build:official # release:pack 要求 official 客户端构建记录 +corepack pnpm exec tsx scripts/release/pack.ts --family vendor --out dist/npm-vendor --concurrency 8 +corepack pnpm exec tsx scripts/release/pack.ts --family dsh --out dist/npm-dsh --concurrency 8 +``` + +产出 **9 个 vendor + 245 个 dsh** tarball,保存在 `packages/harness-0.1.2-alpha.4/`(含各 family 的 `publish-order.txt`)。旧目录 `packages/harness-0.1.2-alpha.3/` 已在同一提交删除。 + +## 核心架构变化与协议影响 + +### 1. 会话 Seq / LogOffset 编译期 Branded Types +上游在 `dsh-session` 中将会话序列号与日志偏移量引入了 TS 编译期 Branded Type(`SessionSeq(number)` / `SessionLogOffset(number)`)。 +- **对 Wire 协议无破坏**:在 HTTP / JSON-RPC / WebSocket wire 传输与 JSONL 文件存储中,依然是标准的 JavaScript number,`session/page` 入参 `throughSeq` / `beforeSeq`、事件对象的 `event.seq` 均为普通数值。 +- **对 API Controller 的影响**:在 TS 签名中,`create(meta, inheritedEventCount?: SessionLogOffset)` 增加了可选偏移量参数;`PersistenceBackend.appendBatch` 入参由 `(meta, events, isMaterialized)` 调整为 `(storage, events, isMaterialized)`。 + +### 2. 模型设置与候选模型实时搜索 +`dsh-client-ui-settings-models` 在 alpha.4 中为候选模型列表增加了过滤能力,相关全选变量重命名为 `allVisibleCandidatesPicked` / `toggleVisibleCandidates`,桌面端已对齐该断言。 + +## 补丁变迁表(全部 19 个补丁) + +| # | 补丁包名 | alpha.4 处置 | 变更说明 | +|---|---|---|---| +| 1 | `@deepseek-ai/cordis-plugin-loader` | 保留 (1.0.3) | Vendor 包版本保持 1.0.3,机械应用通过 | +| 2 | `@deepseek-ai/dsh` | 适配 (alpha.4) | 运行时依赖项对齐 | +| 3 | `@deepseek-ai/dsh-api-session-controller` | 适配 (alpha.4) | 补齐 `agent.d.ts` 的 `AgentHandle` 导入;保留 remote event 补丁 | +| 4 | `@deepseek-ai/dsh-client-modules` | 适配 (alpha.4) | 桌面端动态模块加载对齐 | +| 5 | `@deepseek-ai/dsh-client-ui-agent-preset` | 适配 (alpha.4) | 预设 UI 样式与挂载点对齐 | +| 6 | `@deepseek-ai/dsh-client-ui-chat` | 适配 (alpha.4) | Markdown 渲染与本地路径点击 seam 对齐 | +| 7 | `@deepseek-ai/dsh-client-ui-deliverables` | 适配 (alpha.4) | Bundle 行号漂移重新对齐,保留 localPathReference | +| 8 | `@deepseek-ai/dsh-client-ui-directory-picker-native` | 适配 (alpha.4) | 原生目录选择器钩子 | +| 9 | `@deepseek-ai/dsh-client-ui-layout` | 适配 (alpha.4) | 窗口与托盘布局适配 | +| 10 | `@deepseek-ai/dsh-client-ui-model-selection` | 适配 (alpha.4) | 适配 CSS 样式类(elevation stroke 与圆角),保留模型搜索框 | +| 11 | `@deepseek-ai/dsh-client-ui-settings-models` | 适配 (alpha.4) | 适配上游可见候选模型逻辑与行号对齐 | +| 12 | `@deepseek-ai/dsh-client-ui-sidebar` | 适配 (alpha.4) | 侧边栏布局与状态对齐 | +| 13 | `@deepseek-ai/dsh-client-ui-trajectory` | 适配 (alpha.4) | 轨迹流事件与错误处理对齐 | +| 14 | `@deepseek-ai/dsh-client-ui-workspace` | 适配 (alpha.4) | 适配 CSS module 样式(corner-shape、border),保留未读标记与定位菜单 | +| 15 | `@deepseek-ai/dsh-llm-deepseek` | 适配 (alpha.4) | DeepSeek Provider 错误格式化 | +| 16 | `@deepseek-ai/dsh-llm-pi-ai` | 适配 (alpha.4) | Pi AI Provider 集成适配 | +| 17 | `@deepseek-ai/dsh-session-persistence` | 适配 (alpha.4) | 永久删除能力(PersistenceCoordinator.delete)适配 SessionLogOffset | +| 18 | `@deepseek-ai/dsh-session-persistence-jsonl` | 适配 (alpha.4) | 永久删除存储清理(deleteStored)适配 alpha.4 方法签名 | +| 19 | `@deepseek-ai/dsh-workspace` | 适配 (alpha.4) | 工作区会话忘记(forgetSession)与清理逻辑对齐 | + +## 验证结论 + +- **单元与集成测试**:76 个测试文件、569 个测试用例全部通过(0 失败)。 +- **类型检查**:`npm run typecheck` 0 错误。 +- **构建**:`npm run build` 成功输出 main 与 preload 构建产物。 +- **端到端认证**:`scripts/verify-harness-auth.mjs` 4 阶段全通(令牌捕获、未鉴权 401、换取 cookie 303、带鉴权 200)。 +- **实机与分页冒烟**:Electron 实机启动正常,插件加载无异常;`session/page` 接口在真实 `dsh web` 实例中成功分页获取数据。 diff --git a/docs/harness-0.1.2-upgrade.md b/docs/harness-0.1.2-upgrade.md index 6f19dae5c..3f043068f 100644 --- a/docs/harness-0.1.2-upgrade.md +++ b/docs/harness-0.1.2-upgrade.md @@ -1,5 +1,7 @@ # 升级到 Harness 0.1.2-alpha.1 +> 本文档记录 0.1.1-rc.2 → 0.1.2-alpha.1 的升级。后续见 [harness-0.1.2-alpha.3-upgrade.md](./harness-0.1.2-alpha.3-upgrade.md) 及 [harness-0.1.2-alpha.4-upgrade.md](./harness-0.1.2-alpha.4-upgrade.md)。 + 对照上游 [`dsh-v0.1.2-alpha.1`](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.2-alpha.1) (2026-08-27 发布)从 `0.1.1-rc.2` 升级。 diff --git a/docs/superpowers/plans/2026-08-31-harness-0.1.2-alpha.3-upgrade.md b/docs/superpowers/plans/2026-08-31-harness-0.1.2-alpha.3-upgrade.md new file mode 100644 index 000000000..6bc7d7fd0 --- /dev/null +++ b/docs/superpowers/plans/2026-08-31-harness-0.1.2-alpha.3-upgrade.md @@ -0,0 +1,629 @@ +# Harness 0.1.2-alpha.3 升级 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 dsh-desktop 从锁定的 Harness `dsh-v0.1.2-alpha.1` 升级到 `dsh-v0.1.2-alpha.3`,依赖、补丁、宿主脚本、测试、文档全部适配并通过完整验证。 + +**Architecture:** 上游未发布 npm,继续用仓库内 `file:` tarball 桥接:在本地克隆 `/Users/alex/Documents/Code/AI/deepseek-harness` 构建并打包 alpha.3 产物,提交进 `packages/harness-0.1.2-alpha.3/`,逐项切换 `package.json` 与 lockfile;16 个 `patch-package` 补丁逐个 rebase 到新构建产物并按「上游是否已原生提供」决定保留 / 删除;宿主侧(`scripts/`、`src/main/`)按 alpha.3 实际行为复核。 + +**Tech Stack:** Electron + electron-vite,Vitest,patch-package,npm(应用侧),pnpm@11.7.0 via corepack(上游构建侧),Node 24。 + +## Global Constraints + +- 目标上游标签:`dsh-v0.1.2-alpha.3`(commit `dd6322d604e00eec1ba5e0c8541159906a21094a`)。跳过 alpha.2,直接对比 alpha.1 → alpha.3。 +- 依赖来源:仓库内 `file:packages/harness-0.1.2-alpha.3/...` tarball,**不**换回语义化版本(列入「剩余工作」)。 +- 上游构建目录:`/Users/alex/Documents/Code/AI/deepseek-harness`(已 fetch alpha.3 标签)。 +- 构建工具链:`corepack pnpm`(`packageManager: pnpm@11.7.0`);`pnpm run release:pack -- --family X` 会把参数当位置参数报错,必须直接 `corepack pnpm exec tsx scripts/release/pack.ts --family X`。 +- 验证必须用完整 `npm ci`,**禁止** `--ignore-scripts`(会跳过 `postinstall`,漏掉启动期问题)。 +- 保持排除上游未启用的 provider / 测试包(e2b、lsp、mock-server、replay、claude-code/codex subagent 等),见 `test/release.test.ts` `excludedHarnessPackages`。 +- 补丁判断标准:能力「上游是否已原生提供」→ 是则删补丁并在升级文档记「交还上游」;不是「补丁能不能修好」。 +- 每个补丁文件命名 `@deepseek-ai++0.1.2-alpha.3.patch`(版本号跟随依赖)。 +- 分支:`claude/harness-alpha3-adapt-adfd61`(当前 worktree,已存在)。 +- 完成定义:`npm ci` + `npm run build` + `npm test` + `npm run typecheck` + `node scripts/verify-harness-auth.mjs` + 实际启动 Electron(profile 四个桌面插件全挂载)全部通过。 + +--- + +## 文件结构 + +**上游构建产物(新增目录)** +- `packages/harness-0.1.2-alpha.3/README.md` — 来源标签、构建命令、产物计数 +- `packages/harness-0.1.2-alpha.3/npm-vendor/*.tgz` — cordis / cosmokit / schemastery 等 +- `packages/harness-0.1.2-alpha.3/npm-dsh/*.tgz` — `@deepseek-ai/dsh-*` 全量 +- **删除** `packages/harness-0.1.2-alpha.1/` 整个目录 + +**依赖 / lockfile** +- `package.json` — 全部 `file:` 路径版本切换;删 `dsh-agent-spine-demo`;按闭包结论增删;`market-installer` peer +- `packages/dsh-desktop-market-installer/package.json` — peerDependencies 范围 → `^0.1.2-alpha.3` +- `package-lock.json` — 重新生成 + +**补丁(`patches/`,逐个改名 + rebase)** +- `@deepseek-ai+cordis-plugin-loader+*.patch` +- `@deepseek-ai+dsh+*.patch`(闭包 manifest 注入 + spine-demo 移除 + util 新增) +- `@deepseek-ai+dsh-api-session-controller+*.patch` +- `@deepseek-ai+dsh-client-modules+*.patch` +- `@deepseek-ai+dsh-client-ui-agent-preset+*.patch` +- `@deepseek-ai+dsh-client-ui-chat+*.patch` +- `@deepseek-ai+dsh-client-ui-deliverables+*.patch` +- `@deepseek-ai+dsh-client-ui-directory-picker-native+*.patch` +- `@deepseek-ai+dsh-client-ui-layout+*.patch` +- `@deepseek-ai+dsh-client-ui-model-selection+*.patch` +- `@deepseek-ai+dsh-client-ui-settings-models+*.patch` +- `@deepseek-ai+dsh-client-ui-sidebar+*.patch` +- `@deepseek-ai+dsh-client-ui-trajectory+*.patch` +- `@deepseek-ai+dsh-client-ui-workspace+*.patch` +- `@deepseek-ai+dsh-llm-deepseek+*.patch` +- `@deepseek-ai+dsh-llm-pi-ai+*.patch` + +**宿主脚本 / 主进程** +- `scripts/install-brand-assets.mjs` — alpha.3 前端资源匹配复核 +- `scripts/verify-harness-auth.mjs` — 版本字样 / 行为复核 +- `src/main/runtime/harness-runtime.ts` — 令牌捕获、stderr 就绪探测、`CORE_BUNDLES`、注释版本号 +- `src/main/window-navigation.ts` — 首导航令牌注释 / 逻辑 +- `src/main/mobile/lan-mobile-bridge.ts` — 桥接端点 / 认证换取 + +**测试(硬编码 alpha.1 的用例)** +- `test/release.test.ts` `test/version-info.test.ts` `test/desktop-plugin-closure.test.ts` +- `test/query-rail-patch.test.ts` `test/branding-patch.test.ts` `test/client-modules-resolution-patch.test.ts` +- `test/preset-transfer-patch.test.ts` `test/directory-picker.test.ts` `test/profile-compatibility.test.ts` +- `test/provider-error-patch.test.ts` `test/open-in-finder.test.ts` + +**文档** +- `docs/harness-0.1.2-alpha.3-upgrade.md` — 新增 +- `docs/harness-0.1.2-upgrade.md` — 顶部加历史指向 +- `README.md` `README.zh.md` `README.ja.md` `README.es.md` `README.pt.md` `README.ru.md` — 版本字样 + +--- + +## Task 1: 上游 alpha.3 构建与打包 + +**Files:** +- Create: `packages/harness-0.1.2-alpha.3/README.md`, `packages/harness-0.1.2-alpha.3/npm-vendor/`, `packages/harness-0.1.2-alpha.3/npm-dsh/` +- Delete: `packages/harness-0.1.2-alpha.1/` + +**Interfaces:** +- Produces: `packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai--0.1.2-alpha.3.tgz` 与 `npm-vendor/-.tgz` 全量 tarball,供 Task 2 的 `package.json` `file:` 引用。 + +- [ ] **Step 1: 切到 alpha.3 并装依赖** + +```bash +cd /Users/alex/Documents/Code/AI/deepseek-harness +git checkout dsh-v0.1.2-alpha.3 +corepack enable +corepack pnpm install --frozen-lockfile +``` + +Expected: install 成功,`pnpm --version` 报告 11.7.0。 + +- [ ] **Step 2: 构建 official 客户端** + +```bash +corepack pnpm run build:official +``` + +Expected: 退出 0;生成 official 客户端构建记录(`release:pack` 依赖它)。 + +- [ ] **Step 3: 打包 vendor 与 dsh 两个 family** + +```bash +corepack pnpm exec tsx scripts/release/pack.ts --family vendor --out dist/npm-vendor --concurrency 8 +corepack pnpm exec tsx scripts/release/pack.ts --family dsh --out dist/npm-dsh --concurrency 8 +``` + +Expected: `dist/npm-vendor/` 与 `dist/npm-dsh/` 产出 `.tgz`。记录两目录的文件数(alpha.1 为 9 vendor + 241 dsh,alpha.3 因新增/删除包会有出入)。 + +- [ ] **Step 4: 拷贝产物进本仓库并删除旧目录** + +```bash +cd /Users/alex/Documents/Code/dataelem/dsh-desktop/.claude/worktrees/harness-alpha3-adapt-adfd61 +rm -rf packages/harness-0.1.2-alpha.1 +mkdir -p packages/harness-0.1.2-alpha.3 +cp -R /Users/alex/Documents/Code/AI/deepseek-harness/dist/npm-vendor packages/harness-0.1.2-alpha.3/ +cp -R /Users/alex/Documents/Code/AI/deepseek-harness/dist/npm-dsh packages/harness-0.1.2-alpha.3/ +``` + +- [ ] **Step 5: 写 README** + +`packages/harness-0.1.2-alpha.3/README.md` 内容照 alpha.1 结构:上游标签 `dsh-v0.1.2-alpha.3`、上面 4 步命令、产物计数、「上游发布 npm 后删除本目录」。 + +- [ ] **Step 6: 生成两个 family 的包清单差异记录(供后续任务参考)** + +```bash +comm -3 \ + <(ls /Users/alex/Documents/Code/dataelem/dsh-desktop/packages/harness-0.1.2-alpha.1/npm-dsh 2>/dev/null | sed 's/-0.1.2-alpha.1.tgz//' | sort) \ + <(ls packages/harness-0.1.2-alpha.3/npm-dsh | sed 's/-0.1.2-alpha.3.tgz//' | sort) > /tmp/alpha3-pkg-delta.txt +cat /tmp/alpha3-pkg-delta.txt +``` + +(alpha.1 目录此时已删,改用 git:`git show HEAD:packages/harness-0.1.2-alpha.1/... ` 或从 `package.json` 历史取列表。) +预期至少包含:删 `deepseek-ai-dsh-agent-spine-demo`、删 `deepseek-ai-dsh-session-persistence-sqlite`(本就未引用);新增 `deepseek-ai-dsh-client-ui-schedule`、`deepseek-ai-dsh-util-deque`/`-time`/`-values`(实际名以 tarball 为准)。 + +- [ ] **Step 7: Commit** + +```bash +git add packages/harness-0.1.2-alpha.3 packages/harness-0.1.2-alpha.1 +git commit -m "chore(harness): 打包上游 dsh-v0.1.2-alpha.3 本地 tarball" +``` + +--- + +## Task 2: package.json 依赖切换与 lockfile 重建 + +**Files:** +- Modify: `package.json`(`dependencies` 全部 harness `file:` 行) +- Modify: `packages/dsh-desktop-market-installer/package.json` +- Regenerate: `package-lock.json` + +**Interfaces:** +- Consumes: Task 1 的 `packages/harness-0.1.2-alpha.3/**/*.tgz`。 +- Produces: 可 `npm install` 解析的依赖图,供 Task 3+ 的 `npm ci`。 + +- [ ] **Step 1: 批量替换路径与版本** + +把 `package.json` 中每处 +`file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai--0.1.2-alpha.1.tgz` +→ `file:packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai--0.1.2-alpha.3.tgz`; +`npm-vendor` 段按 tarball 实际版本号替换(vendor 版本号可能未变,路径仍要改 `alpha.1`→`alpha.3`)。 + +- [ ] **Step 2: 删除上游已移除的依赖行** + +删掉 `"@deepseek-ai/dsh-agent-spine-demo": ...`(`package.json:60` 附近)。 +若 Step 6/Task 1 的 delta 显示还有其他被删包出现在 dependencies,一并删除。 + +- [ ] **Step 3: 加入新增闭包包(若需要)** + +用 Task 4 的闭包结论;若此刻未知,先跳过,Task 4 回填后重跑本任务的 lockfile 步骤。 + +- [ ] **Step 4: 放宽 market-installer peer 范围** + +`packages/dsh-desktop-market-installer/package.json` 的 `peerDependencies` 中 +`@deepseek-ai/dsh*` 范围 → `^0.1.2-alpha.3`(预发布 caret 只在同一 `major.minor.patch` 内匹配)。 + +- [ ] **Step 5: 重新生成 lockfile** + +```bash +rm -rf node_modules package-lock.json +npm install --ignore-scripts +``` + +Expected: 生成新的 `package-lock.json`,所有 `@deepseek-ai/*` 指向 `packages/harness-0.1.2-alpha.3/`。 + +- [ ] **Step 6: 完整 ci 验证依赖可解析** + +```bash +rm -rf node_modules +npm ci +``` + +Expected: 退出 0,`postinstall`(`patch-package` + `install-brand-assets` + `install-electron`)此时可能因补丁未适配而失败 —— 若失败在 `patch-package` 阶段属预期,记录失败补丁名,进入 Task 5;若失败在 `install-brand-assets`,进入 Task 6。仅当失败原因是依赖缺失才在本任务修。 + +- [ ] **Step 7: Commit** + +```bash +git add package.json package-lock.json packages/dsh-desktop-market-installer/package.json +git commit -m "chore(deps): 依赖切换到 harness 0.1.2-alpha.3" +``` + +--- + +## Task 3: `@deepseek-ai/dsh` 依赖闭包补丁 + +**Files:** +- Modify: `patches/@deepseek-ai+dsh+0.1.2-alpha.1.patch` → 改名 `patches/@deepseek-ai+dsh+0.1.2-alpha.3.patch` +- Test: `test/desktop-plugin-closure.test.ts` + +**Interfaces:** +- Consumes: Task 1 的 dsh tarball(含 `package.json` manifest)。 +- Produces: 注入了 `dsh-desktop-*` 桌面插件的 dsh manifest,profile 解析依赖它。 + +- [ ] **Step 1: 展开 alpha.3 的 dsh 包 manifest** + +```bash +mkdir -p /tmp/dsh-a3 && tar -xzf packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai-dsh-0.1.2-alpha.3.tgz -C /tmp/dsh-a3 +cat /tmp/dsh-a3/package/package.json +``` + +记录其 `dependencies` 键集合,与 alpha.1 版本(`git show HEAD~2:patches/@deepseek-ai+dsh+0.1.2-alpha.1.patch`)对比。 + +- [ ] **Step 2: 运行现有闭包测试确认它失败** + +```bash +npx vitest run test/desktop-plugin-closure.test.ts +``` + +Expected: FAIL(补丁文件名还是 alpha.1,或 manifest 行号漂移)。 + +- [ ] **Step 3: 重建补丁** + +```bash +git mv patches/@deepseek-ai+dsh+0.1.2-alpha.1.patch patches/@deepseek-ai+dsh+0.1.2-alpha.3.patch +rm -rf node_modules/@deepseek-ai/dsh && npm ci --ignore-scripts +# 手动编辑 node_modules/@deepseek-ai/dsh/package.json: +# - 加回 dsh-desktop-client-ui / dsh-desktop-preset-transfer / dsh-desktop-hmr-fallback / dsh-desktop-market-installer(保持 alpha.1 补丁里那份清单) +# - 若 alpha.1 补丁还改了别的文件(行号偏移),同步 +# - 删除 spine-demo 相关行(若上游 manifest 已无则无需动) +npx patch-package @deepseek-ai/dsh --patch-dir patches +``` + +Expected: 生成 `patches/@deepseek-ai+dsh+0.1.2-alpha.3.patch`。 + +- [ ] **Step 4: 若测试要求同步 package.json / dsh 闭包三处一致,更新测试夹具** + +`test/desktop-plugin-closure.test.ts` 锁定不变量:profile patch 里每个 `dsh-desktop-*` 行必须同时在 dsh 依赖补丁与 `package.json` dependencies 里。若测试内硬编码 `0.1.2-alpha.1` 路径,改为 `0.1.2-alpha.3`。 + +- [ ] **Step 5: 跑测试确认通过** + +```bash +npx vitest run test/desktop-plugin-closure.test.ts +``` + +Expected: PASS。 + +- [ ] **Step 6: Commit** + +```bash +git add patches/ test/desktop-plugin-closure.test.ts +git commit -m "fix(patch): dsh 闭包补丁适配 alpha.3" +``` + +--- + +## Task 4: 新增 `util/*` 与 `client/ui-schedule` 闭包判定 + +**Files:** +- Modify(按需): `package.json`, `patches/@deepseek-ai+dsh+0.1.2-alpha.3.patch`, `test/release.test.ts` + +**Interfaces:** +- Consumes: Task 1 tarball,Task 3 的 dsh manifest。 +- Produces: 明确结论——新增包是否进 dependencies / 排除清单;lockfile 中 `@deepseek-ai/dsh-*` 全部 `file:` 解析。 + +**Task 2 审查发现(硬约束,本任务必须消除):** +Task 2 重建的 lockfile 里,5 个上游新增包 `dsh-deque`、`dsh-util-time`、`dsh-util-values`、 +`dsh-client-ui-schedule`、`dsh-session-turn-outline` 目前从 `registry.npmmirror.com` +远端解析(它们是已交付包的传递依赖,但 package.json 未引用 → npm 走网络)。 +这违反「依赖来源 = 仓库内 `file:` tarball」全局约束,离线 / 纯仓库环境会失败。 +它们的 tarball 其实已在 `packages/harness-0.1.2-alpha.3/npm-dsh/`。 + +- [ ] **Step 1: 确认这 5 个包确为交付闭包的传递依赖** + +```bash +mkdir -p /tmp/dsh-a3 && tar -xzf packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai-dsh-0.1.2-alpha.3.tgz -C /tmp/dsh-a3 +grep -rE "dsh-deque|dsh-util-time|dsh-util-values|dsh-client-ui-schedule|dsh-session-turn-outline" packages/harness-0.1.2-alpha.3/npm-dsh/*/package.json 2>/dev/null || true +# 逐个解包相关 tarball 查 dependencies,确认谁在依赖它们 +npm ls @deepseek-ai/dsh-deque @deepseek-ai/dsh-util-time @deepseek-ai/dsh-util-values @deepseek-ai/dsh-client-ui-schedule @deepseek-ai/dsh-session-turn-outline 2>&1 | head -40 +``` + +- [ ] **Step 2: 把 5 个包加入 `package.json` dependencies 为 `file:` 依赖** + +对每个包加一行(按字母序插入到 `dependencies` 中对应位置): +``` +"@deepseek-ai/dsh-deque": "file:packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai-dsh-deque-0.1.2-alpha.3.tgz", +"@deepseek-ai/dsh-util-time": "file:packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai-dsh-util-time-0.1.2-alpha.3.tgz", +"@deepseek-ai/dsh-util-values": "file:packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai-dsh-util-values-0.1.2-alpha.3.tgz", +"@deepseek-ai/dsh-client-ui-schedule": "file:packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai-dsh-client-ui-schedule-0.1.2-alpha.3.tgz", +"@deepseek-ai/dsh-session-turn-outline": "file:packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai-dsh-session-turn-outline-0.1.2-alpha.3.tgz", +``` +先 `ls packages/harness-0.1.2-alpha.3/npm-dsh/ | grep -E 'deque|util-time|util-values|ui-schedule|turn-outline'` 核对确切文件名(Task 1 报告:`dsh-deque` 而非 `dsh-util-deque`)。 + +- [ ] **Step 3: 重建 lockfile 并验证无 registry 解析** + +```bash +rm -f package-lock.json && npm install --ignore-scripts +grep -nE '"resolved": "https?://[^"]*deepseek-ai[/-]dsh' package-lock.json +``` +Expected: 上面 grep **无输出**(或仅剩 Task 2 前就存在的非本次条目,需逐个说明)。 +`grep -c "harness-0.1.2-alpha.1" package-lock.json` = 0。 + +- [ ] **Step 3b: 完整 `npm ci` 回归** + +```bash +rm -rf node_modules && npm ci +``` +Expected: 退出 0(patch-package 仍会报 Task 5 未适配的补丁,属预期)。 + +- [ ] **Step 4: 清理 `session-persistence-sqlite` 排除项** + +`test/release.test.ts` `excludedHarnessPackages` 中 `@deepseek-ai/dsh-session-persistence-sqlite`:上游已删包,保留断言仍成立但语义过时。确认 `dsh-storage-sqlite` 是否仍作为 `session-query-sqlite` 的传递依赖存在于 lockfile —— 若存在则保留排除项,否则一并说明。按实际情况决定删除或加注释。 + +- [ ] **Step 5: 跑 release + 闭包测试** + +```bash +npx vitest run test/release.test.ts test/desktop-plugin-closure.test.ts +``` + +Expected: PASS(`release.test.ts` 现在还会校验 5 个新增包已作为 `file:` 依赖列出且 lockfile 不含 registry 解析的 `@deepseek-ai/dsh-*`;若该断言不存在则新增之)。 + +- [ ] **Step 6: Commit** + +```bash +git add package.json package-lock.json test/release.test.ts patches/ +git commit -m "chore(deps): 处理 alpha.3 新增 util 包与 sqlite 排除项" +``` + +--- + +## Task 5: 逐个 rebase 15 个组件补丁 + +对以下每个补丁重复本任务的步骤循环(**一个补丁一个 commit**): +`cordis-plugin-loader`, `dsh-api-session-controller`, `dsh-client-modules`, +`dsh-client-ui-agent-preset`, `dsh-client-ui-chat`, `dsh-client-ui-deliverables`, +`dsh-client-ui-directory-picker-native`, `dsh-client-ui-layout`, +`dsh-client-ui-model-selection`, `dsh-client-ui-settings-models`, +`dsh-client-ui-sidebar`, `dsh-client-ui-trajectory`, `dsh-client-ui-workspace`, +`dsh-llm-deepseek`, `dsh-llm-pi-ai`。 + +**Files(每次一个):** +- Rename: `patches/@deepseek-ai++0.1.2-alpha.1.patch` → `+0.1.2-alpha.3.patch` +- Test: 对应的 `test/*-patch.test.ts`(若有) + +**Interfaces:** +- Consumes: Task 1 对应 tarball。 +- Produces: 在 alpha.3 构建产物上干净应用的补丁。 + +- [ ] **Step 1: 装未打补丁的目标包** + +```bash +rm -rf node_modules/@deepseek-ai/ +npm ci --ignore-scripts # 或针对性重装 +``` + +- [ ] **Step 2: 尝试旧补丁,观察失败** + +```bash +git mv patches/@deepseek-ai++0.1.2-alpha.1.patch patches/@deepseek-ai++0.1.2-alpha.3.patch +npx patch-package @deepseek-ai/ --patch-dir patches --error-on-fail +``` + +Expected: 失败或部分 hunk 拒绝(行号 / hash 漂移),或干净应用(仅版本号改名即可)。 + +- [ ] **Step 3: 判定补丁去留** + +对照上游 alpha.3 源码(`/Users/alex/Documents/Code/AI/deepseek-harness` 对应包)判断该补丁修的能力是否已被上游原生提供: +- **已原生提供**(如 alpha.1 时的 turnNavigation / access.confirm / directoryPicker 拆服务)→ `git rm` 补丁,在升级文档「交还上游」记一行,跳到 Step 6。 +- **仍需要** → 继续 Step 4。 + +- [ ] **Step 4: 在解包目录里手动重做改动** + +在 `node_modules/@deepseek-ai/` 里按补丁原意重新编辑(跟随上游改名 / CSS module hash / 新增参数),然后: + +```bash +npx patch-package @deepseek-ai/ --patch-dir patches +``` + +- [ ] **Step 5: 跑对应补丁测试** + +```bash +npx vitest run test/-patch.test.ts # 若存在;否则 npx vitest run 相关用例 +``` + +更新测试里硬编码的 `0.1.2-alpha.1` 路径与漂移的 hash / 行号断言。 +Expected: PASS。 + +- [ ] **Step 6: Commit(单补丁)** + +```bash +git add patches/ test/ +git commit -m "fix(patch): 适配 alpha.3" +``` + +--- + +## Task 6: `install-brand-assets.mjs` 前端资源匹配复核 + +**Files:** +- Modify: `scripts/install-brand-assets.mjs` +- Test: 相关 branding 测试(`test/branding-patch.test.ts` 或脚本自测) + +**Interfaces:** +- Consumes: alpha.3 的 `@deepseek-ai/dsh-web-frontend` 前端资源。 +- Produces: 成功改写品牌资源、目标缺失仍响亮失败的 postinstall 步骤。 + +- [ ] **Step 1: 比对 alpha.3 前端资源结构** + +```bash +mkdir -p /tmp/wf-a3 && tar -xzf packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai-dsh-web-frontend-0.1.2-alpha.3.tgz -C /tmp/wf-a3 +grep -n "favicon" /tmp/wf-a3/package/**/index.html +cat /tmp/wf-a3/package/**/manifest*.json 2>/dev/null +``` + +对照 `scripts/install-brand-assets.mjs` 里的匹配字符串 / 标签选择器。 + +- [ ] **Step 2: 运行脚本观察结果** + +```bash +node scripts/install-brand-assets.mjs +``` + +Expected: 成功,或在某匹配处响亮抛错。 + +- [ ] **Step 3: 若匹配落空,按「标签 / JSON 键」而非固定字符串修** + +保持「目标缺失 → 抛错 → 中断 postinstall」的设计(alpha.1 教训:静默跳过会漏装 Electron)。 + +- [ ] **Step 4: 完整 postinstall 验证** + +```bash +rm -rf node_modules && npm ci +``` + +Expected: 退出 0,Electron 二进制已装,品牌资源已改写。 + +- [ ] **Step 5: Commit** + +```bash +git add scripts/install-brand-assets.mjs test/ +git commit -m "fix(scripts): install-brand-assets 适配 alpha.3 前端资源" +``` + +--- + +## Task 7: 主进程运行时与移动端桥接复核 + +**Files:** +- Modify(按需): `src/main/runtime/harness-runtime.ts`, `src/main/window-navigation.ts`, `src/main/mobile/lan-mobile-bridge.ts` +- Test: `test/lan-mobile-tunnel.test.ts`, `test/session-create-remote-event-patch.test.ts`, `test/query-rail-patch.test.ts`, `test/profile-compatibility.test.ts` + +**Interfaces:** +- Consumes: alpha.3 `dsh web` 输出行格式、`/api/*` 端点、`/api/remote.mux` 流协议。 +- Produces: 令牌捕获、就绪探测、桥接 RPC 与 alpha.3 对齐。 + +- [ ] **Step 1: 跑主进程相关测试基线** + +```bash +npx vitest run test/lan-mobile-tunnel.test.ts test/session-create-remote-event-patch.test.ts test/query-rail-patch.test.ts test/profile-compatibility.test.ts test/gpu-fallback.test.ts +``` + +记录失败项。 + +- [ ] **Step 2: 比对 alpha.3 `dsh-web-app` / `dsh-client-connection` 源码** + +在 `/Users/alex/Documents/Code/AI/deepseek-harness` 查: +- `dsh web:` 令牌输出行格式是否变(`harness-runtime.ts` 捕获正则) +- headless 进度是否仍在 stderr、就绪 / 错误归类字符串 +- `/api//` 端点、`args` 包裹、`session/page` 游标分页、`remote.mux` 流名 是否再变(`lan-mobile-bridge.ts`) +- `--trusted-host` 默认值对 `--host 127.0.0.1` 的影响 + +- [ ] **Step 3: 按 diff 调整实现** + +改动处更新代码注释里的 `0.1.2-alpha.1` → `0.1.2-alpha.3`(`harness-runtime.ts:161,306`、`window-navigation.ts:16`、`lan-mobile-bridge.ts:46,794`)。 + +- [ ] **Step 4: 跑测试确认通过** + +```bash +npx vitest run test/lan-mobile-tunnel.test.ts test/session-create-remote-event-patch.test.ts test/query-rail-patch.test.ts test/profile-compatibility.test.ts +``` + +Expected: PASS(或与 alpha.1 基线相同的已知例外)。 + +- [ ] **Step 5: Commit** + +```bash +git add src/main/ test/ +git commit -m "fix(main): 运行时与移动端桥接复核 alpha.3" +``` + +--- + +## Task 8: 全量测试 + typecheck + 版本字样清理 + +**Files:** +- Modify: `test/version-info.test.ts` 及其他残留硬编码 `0.1.2-alpha.1` 的测试 +- Modify: `README.md` `README.zh.md` `README.ja.md` `README.es.md` `README.pt.md` `README.ru.md` + +- [ ] **Step 1: 全量搜索残留版本字样** + +```bash +grep -rn "0.1.2-alpha.1\|harness-0.1.2-alpha.1" --include="*.ts" --include="*.md" --include="*.mjs" --include="*.json" . | grep -v node_modules | grep -v packages/harness-0.1.2-alpha.3 | grep -v docs/harness-0.1.2-upgrade.md | grep -v docs/superpowers +``` + +逐条改为 `0.1.2-alpha.3`(`docs/harness-0.1.2-upgrade.md` 作为历史记录保留原文)。 + +- [ ] **Step 2: 全量测试** + +```bash +npm test +``` + +Expected: 通过数 ≥ alpha.1 基线(454 tests / 61 files 附近,会因增删略变);记录已知的 WebSocket teardown 退出码 1。 + +- [ ] **Step 3: typecheck** + +```bash +npm run typecheck +``` + +Expected: 0 错误。 + +- [ ] **Step 4: build** + +```bash +npm run build +``` + +Expected: ok。 + +- [ ] **Step 5: Commit** + +```bash +git add -A +git commit -m "chore: 清理 alpha.1 版本字样,全量测试通过" +``` + +--- + +## Task 9: 认证链路与 Electron 实机验证 + +- [ ] **Step 1: verify-harness-auth** + +```bash +node scripts/verify-harness-auth.mjs +``` + +Expected: 401 → 303 → 200 全通过,含「不带 cookie 必须 401」。 + +- [ ] **Step 2: 实际启动 Electron** + +```bash +npm run build && npx electron-vite preview +``` + +(或项目既有的启动方式;参照 `run` skill / `docs/development.md`。) +Expected: 应用正常运行,profile 中 4 个桌面插件(client-ui / preset-transfer / hmr-fallback / market-installer)全部挂载,无 `Cannot find package 'dsh-desktop-*'` 报错。 + +- [ ] **Step 3: 手动冒烟** + +新建会话、发一条 prompt、打开设置 → 模型页、侧边栏品牌显示正确、手机入口在设置行最右。历史会话列表正常显示(验证 SQLite persistence 移除无影响)。 + +- [ ] **Step 4: 记录验证结果**(写入 Task 10 的升级文档,失败也照记) + +--- + +## Task 10: 升级文档 + +**Files:** +- Create: `docs/harness-0.1.2-alpha.3-upgrade.md` +- Modify: `docs/harness-0.1.2-upgrade.md`(顶部加「本文档为历史记录,最新见 alpha.3」) + +- [ ] **Step 1: 写 `docs/harness-0.1.2-alpha.3-upgrade.md`** + +结构照 alpha.1 文档: +- **状态**:`npm ci` / `build` / `test`(数字)/ `typecheck` / `verify-harness-auth` / Electron 实机 的结果 +- **本地打包**:Task 1 的命令与产物计数 +- **上游结构性变更**:删 `session-persistence-sqlite`(+ 桌面端零影响说明)、删 `agent-spine-demo`(+ `dsh-sdk-minimal` 内联)、新增 `ui-schedule` / `util/*`(+ 闭包判定结论) +- **会话持久化**:JSONL-only,历史会话可见性不受影响,「旧版本导出」仅对上游 CLI sqlite 用户 +- **主机 API / 桥接**:alpha.3 是否再变(Task 7 结论) +- **补丁清单**:新增 / 删除 / 仅偏移 三类表格(Task 3/5 结果) +- **踩到的问题**:构建 / 补丁 / 启动期 +- **剩余工作**:上游发 npm 后换回语义化版本 + 重生成 lockfile(照抄 alpha.1 的 4 步) + +- [ ] **Step 2: Commit** + +```bash +git add docs/ +git commit -m "docs: harness 0.1.2-alpha.3 升级记录" +``` + +--- + +## Self-Review + +**Spec 覆盖检查:** +- 本地打包桥接 → Task 1 ✓ +- 依赖切换 + lockfile → Task 2 ✓ +- market-installer peer → Task 2 Step 4 ✓ +- 删 spine-demo → Task 2 Step 2 ✓ +- 新增 util 包闭包判定 → Task 4 ✓ +- session-persistence-sqlite 排除项清理 → Task 4 Step 3 ✓ +- dsh 闭包补丁 → Task 3 ✓ +- 15 个组件补丁 rebase + 去留判定 → Task 5 ✓ +- install-brand-assets → Task 6 ✓ +- harness-runtime / 桥接 → Task 7 ✓ +- 版本字样清理 → Task 8 ✓ +- 全量验证(ci/build/test/tsc/auth/Electron)→ Task 8 + 9 ✓ +- 升级文档 → Task 10 ✓ +- README 更新 → Task 8 Step 1 ✓ + +**Placeholder 扫描:** Task 4 Step 2 / Task 5 Step 3-4 是判定分支而非固定代码——这是依赖升级任务的固有性质(补丁内容依赖尚未生成的 alpha.3 构建产物)。每个分支都给了明确的判定标准(「上游是否已原生提供」)和可执行命令,非占位符。 + +**类型一致性:** 无跨任务函数签名;`packages/harness-0.1.2-alpha.3/` 路径、补丁命名 `+0.1.2-alpha.3.patch`、`^0.1.2-alpha.3` peer 范围在全文一致。 + +**已知偏离标准 TDD 的地方:** 依赖 / 补丁类任务无法「先写失败测试再写实现」——改为「跑既有测试基线 → 改动 → 跑测试确认」,既有测试套件(`*-patch.test.ts`、`release.test.ts`、`desktop-plugin-closure.test.ts`)就是回归网。 diff --git a/docs/superpowers/plans/2026-09-01-harness-0.1.2-alpha.4-upgrade.md b/docs/superpowers/plans/2026-09-01-harness-0.1.2-alpha.4-upgrade.md new file mode 100644 index 000000000..a3d6f763b --- /dev/null +++ b/docs/superpowers/plans/2026-09-01-harness-0.1.2-alpha.4-upgrade.md @@ -0,0 +1,367 @@ +# Harness 0.1.2-alpha.4 升级 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax. + +**Goal:** 把 dsh-desktop 从已适配的 Harness `dsh-v0.1.2-alpha.3` 推进到 `dsh-v0.1.2-alpha.4`,依赖 / 19 补丁 / 宿主脚本 / 移动端桥接 / 测试 / 文档全部适配并通过完整验证。 + +**Architecture:** 与 alpha.3 升级同形状——本地 clone 构建 alpha.4 tarball 桥接,`file:` 依赖切换,19 个 `patch-package` 补丁逐个 rebase,宿主侧按 alpha.4 实际行为复核(会话 branding 重构重点看移动端 wire)。 + +**Tech Stack:** Electron + electron-vite,Vitest,patch-package,npm(应用侧),pnpm@11.7.0 via corepack(上游构建),Node 24。 + +## Global Constraints + +- 目标上游标签 `dsh-v0.1.2-alpha.4`(commit `4e84901e64`)。从 alpha.3 直接推进,跳过独立合并 alpha.3。 +- 依赖来源:仓库内 `file:packages/harness-0.1.2-alpha.4/...` tarball,**不**换回语义化版本。 +- 上游构建目录 `/Users/alex/Documents/Code/AI/deepseek-harness`(已 fetch alpha.4 标签,当前 detached 在 alpha.3)。 +- 构建工具链 `corepack pnpm`;pack 必须 `corepack pnpm exec tsx scripts/release/pack.ts --family X`(`pnpm run release:pack -- --family X` 会报错)。 +- 验证必须完整 `npm ci`,**禁止** `--ignore-scripts` 作最终验证。 +- 保持排除上游未启用的 provider / 测试包(e2b、lsp、mock-server、replay、subagent-claude-code/codex、web-search-exa/perplexity、code-runtime-python 等)。 +- 补丁命名 `@deepseek-ai++0.1.2-alpha.4.patch`(cordis-plugin-loader 用其 vendor 版本号)。`test/patch-path.ts` `patchPath()` 要求每包恰好 1 个 `.patch` 文件。 +- 补丁去留标准:「上游是否已原生提供」→ 是则 `git rm` + 升级文档记「交还上游」。 +- `test/desktop-plugin-closure.test.ts` 不变量:每个 `dsh-desktop-*` 行同时在 profile patch yml、`@deepseek-ai+dsh` 补丁、`package.json` `file:` 依赖三处。 +- 4 个 CSS 补丁(agent-preset / model-selection / settings-models / workspace)hunk 里的 `//#region \0dsh-css:<绝对路径>` context 行——不要手改补丁体,沿用 alpha.3 已有的文档说明。 +- **⚠️ worktree 隔离**:所有实施只在 `/Users/alex/Documents/Code/dataelem/dsh-desktop/.claude/worktrees/harness-alpha3-adapt-adfd61`(branch `claude/harness-alpha3-adapt-adfd61`)内操作。**绝不** `cd` 进主检出 `/Users/alex/Documents/Code/dataelem/dsh-desktop`(不同分支,用户 WIP)。每个 `git commit` / `npm ci` 前 `pwd` 确认。上游 harness repo 只读。 +- 会话 branding 重构(`SessionSeq` / `SessionLogOffset`)是编译期 TS 变更;上游 Agent Note 明确 wire / v0 JSONL / API 数值表示不变。移动端桥接以实测为准。 +- 完成定义:`npm ci` + `npm run build` + `npm test` + `npm run typecheck` + `node scripts/verify-harness-auth.mjs` + Electron 实机(4 插件挂载 + 会话历史分页冒烟)全绿。 + +--- + +## 文件结构 + +- **新增** `packages/harness-0.1.2-alpha.4/{npm-vendor,npm-dsh}/*.tgz` + `README.md`;**删除** `packages/harness-0.1.2-alpha.3/` +- `package.json`、`packages/dsh-desktop-{client-ui,preset-transfer,market-installer}/package.json`、`package-lock.json` +- `patches/` 19 个文件全部改名 + rebase(含 f13001b 新增的 session-persistence/-jsonl/dsh-workspace) +- `scripts/install-brand-assets.mjs`(按需)、`scripts/verify-harness-auth.mjs`(版本注释) +- `src/main/runtime/harness-runtime.ts`、`src/main/mobile/lan-mobile-bridge.ts`、`src/main/window-navigation.ts`(按需,多半只有注释) +- `test/`:`release.test.ts`、`desktop-plugin-closure.test.ts`、`*-patch.test.ts`、`preset-transfer-patch.test.ts`、`profile-compatibility.test.ts`、`readme-parity.test.ts`、`session-create-remote-event-patch.test.ts`、`lan-mobile-*.test.ts`、`open-in-finder.test.ts` +- `packages/dsh-desktop-preset-transfer/index.js`(`PRESET_SOURCE_DSH_VERSION`) +- 6 份 `README*.md` +- **新增** `docs/harness-0.1.2-alpha.4-upgrade.md`;`docs/harness-0.1.2-alpha.3-upgrade.md` 顶部交叉引用 + +--- + +## Task 1: 上游 alpha.4 构建与打包 + +**Files:** Create `packages/harness-0.1.2-alpha.4/**`; Delete `packages/harness-0.1.2-alpha.3/` + +**Interfaces:** Produces `packages/harness-0.1.2-alpha.4/npm-{dsh,vendor}/*.tgz` 全量,供后续任务 `file:` 引用。 + +- [ ] **Step 1: 切 alpha.4 装依赖** +```bash +cd /Users/alex/Documents/Code/AI/deepseek-harness +git checkout dsh-v0.1.2-alpha.4 +corepack pnpm install --frozen-lockfile +``` +Expected: 成功。若 frozen 失败报 BLOCKED,不自行降级。 + +- [ ] **Step 2: 构建 official** +```bash +corepack pnpm run build:official +``` +Expected: 退出 0。 + +- [ ] **Step 3: 打包** +```bash +corepack pnpm exec tsx scripts/release/pack.ts --family vendor --out dist/npm-vendor --concurrency 8 +corepack pnpm exec tsx scripts/release/pack.ts --family dsh --out dist/npm-dsh --concurrency 8 +``` +记录 vendor / dsh tarball 数(alpha.3 为 9 + 244;alpha.4 净 −1 包,预期 dsh ≈ 243)。 + +- [ ] **Step 4: 拷入 worktree 并删旧目录** +```bash +cd /Users/alex/Documents/Code/dataelem/dsh-desktop/.claude/worktrees/harness-alpha3-adapt-adfd61 +pwd # 确认在 worktree +rm -rf packages/harness-0.1.2-alpha.3 +mkdir -p packages/harness-0.1.2-alpha.4 +cp -R /Users/alex/Documents/Code/AI/deepseek-harness/dist/npm-vendor packages/harness-0.1.2-alpha.4/ +cp -R /Users/alex/Documents/Code/AI/deepseek-harness/dist/npm-dsh packages/harness-0.1.2-alpha.4/ +``` + +- [ ] **Step 5: README** —— 照 alpha.3 的 `packages/harness-0.1.2-alpha.3/README.md` 结构,上游标签 `dsh-v0.1.2-alpha.4` + commit + 4 步命令 + 产物计数 + 「上游发 npm 后删除本目录」。顶部指向 `../../docs/harness-0.1.2-alpha.4-upgrade.md`。 + +- [ ] **Step 6: 包 delta 记录** +```bash +comm -3 \ + <(git show HEAD:package.json | grep -oE 'deepseek-ai-dsh[a-z-]*-0.1.2-alpha.3' | sed 's/-0.1.2-alpha.3//' | sort -u) \ + <(ls packages/harness-0.1.2-alpha.4/npm-dsh | sed 's/-0.1.2-alpha.4.tgz//' | sort -u) +``` +预期:`dsh-tool-subagent-report` 消失(本就排除);无 dsh-desktop 已引用包被删。vendor 段版本号逐一记录。 + +- [ ] **Step 7: Commit** +```bash +git add packages/harness-0.1.2-alpha.4 packages/harness-0.1.2-alpha.3 +git commit -m "chore(harness): 打包上游 dsh-v0.1.2-alpha.4 本地 tarball" +``` + +--- + +## Task 2: package.json 依赖切换 + peer + lockfile + +**Files:** `package.json`, `packages/dsh-desktop-{client-ui,preset-transfer,market-installer}/package.json`, `package-lock.json` + +**Interfaces:** Consumes Task 1 tarball。Produces 可 `npm ci` 解析、全 `file:` 的依赖图。 + +- [ ] **Step 1: 批量替换 dsh 路径** +`package.json` 每处 `file:packages/harness-0.1.2-alpha.3/npm-dsh/deepseek-ai--0.1.2-alpha.3.tgz` → `.../harness-0.1.2-alpha.4/npm-dsh/deepseek-ai--0.1.2-alpha.4.tgz`。逐一确认目标 tarball 存在于 `packages/harness-0.1.2-alpha.4/npm-dsh/`。 + +- [ ] **Step 2: vendor 段替换** +`npm-vendor` 段(`@deepseek-ai/cordis`、`cordis-plugin-*`、`cosmokit`、`schemastery`):目录名换 + 版本号按 `ls packages/harness-0.1.2-alpha.4/npm-vendor/` 实际文件名跟随(可能未变)。 + +- [ ] **Step 3: 删上游已移除依赖(Task 1 已确认)** +删除 `package.json` 的 `"@deepseek-ai/dsh-tool-subagent-report": "file:..."` 行(约第 245 行)—— +alpha.4 上游删除了该包,`packages/harness-0.1.2-alpha.4/npm-dsh/` 无对应 tarball,不删会导致 `npm install` 解析失败。 +代码中若有 `dsh-tool-subagent-report` 引用一并处理(`grep -rn "tool-subagent-report" src/ build/ packages/dsh-desktop-*/`)。 +`dsh-code-runtime-python` 本就不是依赖(在 `test/release.test.ts:103` 排除清单),无需动 package.json。 + +- [ ] **Step 4: peer 范围** +`packages/dsh-desktop-{client-ui,preset-transfer,market-installer}/package.json` 的每个 `@deepseek-ai/dsh*` peer / dependencies 条目 `^0.1.2-alpha.3` → `^0.1.2-alpha.4`。`@deepseek-ai/cordis` `^4.x` 不动。 + +- [ ] **Step 5: 重生成 lockfile** +```bash +pwd # worktree +rm -rf node_modules package-lock.json +npm install --ignore-scripts +grep -c "harness-0.1.2-alpha.3" package-lock.json # = 0 +grep -nE '"resolved": "https?://[^"]*deepseek-ai[/-]dsh' package-lock.json # 空 +``` + +- [ ] **Step 6: 完整 npm ci** +```bash +rm -rf node_modules && npm ci +``` +Expected: 退出 0。`patch-package` 阶段会报未适配的补丁——记录失败补丁名(进 Task 3)。仅依赖解析失败才在本任务修。 + +- [ ] **Step 7: Commit** +```bash +git add package.json package-lock.json packages/dsh-desktop-*/package.json +git commit -m "chore(deps): 依赖切换到 harness 0.1.2-alpha.4" +``` + +--- + +## Task 3: `@deepseek-ai/dsh` 闭包补丁 + +**Files:** `patches/@deepseek-ai+dsh+0.1.2-alpha.3.patch` → `+0.1.2-alpha.4.patch`; `test/desktop-plugin-closure.test.ts`(按需) + +- [ ] **Step 1** 解包 `packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-0.1.2-alpha.4.tgz` 到 `/tmp`,读 `package.json` 的 `dependencies`,与 alpha.3 版本对比。 +- [ ] **Step 2** `npx vitest run test/desktop-plugin-closure.test.ts` —— 观察失败。 +- [ ] **Step 3** `git mv` 改名。`npm ci --ignore-scripts` 拿 pristine dsh,手动加回 4 个 `dsh-desktop-*` 依赖行(位置照旧),`npx patch-package @deepseek-ai/dsh --patch-dir patches` 重生成(若 patch-package regen 因 `file:` 失败,用 `git diff --no-index` 手工生成并修 header 路径)。 +- [ ] **Step 4** 测试内若硬编码 `0.1.2-alpha.3` 路径改为 `alpha.4`(多半版本无关,先查)。 +- [ ] **Step 5** `npx vitest run test/desktop-plugin-closure.test.ts` → PASS。 +- [ ] **Step 6** Commit `fix(patch): dsh 闭包补丁适配 alpha.4`。 + +--- + +## Task 4: sqlite / subagent-report 排除项 + +**Files:** `test/release.test.ts`, `package-lock.json`(若需) + +- [ ] **Step 1** `test/release.test.ts` `excludedHarnessPackages`:确认 `@deepseek-ai/dsh-tool-subagent-report` 是否在该清单——**它其实不在**(Task 1 确认它曾是直接依赖,由 Task 2 删除)。若测试有其它地方引用它则清理。 +- [ ] **Step 1b** `@deepseek-ai/dsh-code-runtime-python`(清单第 103 行):alpha.4 上游把它移到 `packages/experimental/`,不再进 `dsh` family tarball 集。排除断言 `expect(packageJson.dependencies[name]).toBeUndefined()` + lockfile 断言仍成立(它本就不是依赖)——保留该行,加注释说明现状(上游已移到 experimental,桌面端继续不打包)。 +- [ ] **Step 1c** `test/release.test.ts` 约 76-81 行的「vendors upstream-new closure packages」用例里有 2 处硬编码正则 `/^file:packages\/harness-0\.1\.2-alpha\.3\/npm-dsh\/...` —— 改为 `alpha\.4`(alpha.3 Task 4 加的闭包包来源断言,Task 3 已确认此处 fail)。 +- [ ] **Step 2** 复核 `dsh-storage-sqlite` / `dsh-session-persistence-sqlite` 现状注释仍准确(alpha.3 遗留)。 +- [ ] **Step 3** 若上游新增了应排除的 provider 包,加进清单。全仓再 `grep -rn "harness-0.1.2-alpha.3\|0.1.2-alpha.3" test/ | grep -v "// "` 扫一遍非注释的硬编码,本任务能顺手清的清(其余归 Task 8)。 +- [ ] **Step 4** `npx vitest run test/release.test.ts test/desktop-plugin-closure.test.ts` → PASS。 +- [ ] **Step 5** Commit `chore(test): alpha.4 排除清单 + 闭包包断言更新`。 + +--- + +> **补丁总数 = 19**(不是 16)。分支在 alpha.3 完成后被另一位开发者 commit `f13001b` +> "feat(session): port permanent deletion to alpha.3" 扩充:新增 3 个补丁 +> (`dsh-session-persistence`、`dsh-session-persistence-jsonl`、`dsh-workspace`)、 +> 大幅扩写 2 个补丁(`dsh-api-session-controller` +1084、`dsh-client-ui-workspace` +637)、 +> 新增 `test/session-delete-patch.test.ts`。**alpha.4 必须一并把这 19 个带过去。** +> +> Task 2 的 `npm ci` 实测:8 个 apply 失败(需真 rebase),11 个仅版本号警告(改名 + 可能行号)。 + +### 永久删除功能(f13001b)跨补丁怎么落 +- `dsh-session-persistence` / `-jsonl`:`PersistenceCoordinator.delete()`、`SessionPreparations.assertDeletable()`、backend `deleteStored`。**正落在 alpha.4 会话 branding 重构区**(`SessionSeq`/`SessionLogOffset`、`SessionPreparations`、`PersistenceCoordinator`)——重点。 +- `dsh-api-session-controller`:删除 RPC(`session/delete` 之类),释放活动 Agent,拒绝子 Agent / capability 持有的会话。 +- `dsh-workspace`:删除后清理会话索引。 +- `dsh-client-ui-workspace`:删除前确认弹窗(工作区文件保留、不可撤销)+ 错误展示。 +- `test/session-delete-patch.test.ts`(101 行):锁这些补丁的关键字符串,验证时必须过。 + +--- + +## Task 5a: 10 个"仅版本号警告"补丁改名 + 按需 regen + +范围(Task 2 实测 apply-clean,仅 patch 文件名版本不符): +`cordis-plugin-loader`(→ `+1.0.3.patch`,vendor 版本未变,仅 alpha 无关;实际**无需改名**,确认即可)、 +`dsh-client-modules`、`dsh-client-ui-directory-picker-native`、`dsh-client-ui-layout`、 +`dsh-client-ui-sidebar`、`dsh-client-ui-trajectory`、`dsh-client-ui-agent-preset`、 +`dsh-llm-deepseek`、`dsh-llm-pi-ai`、`dsh-workspace`。一补丁一 commit。 + +- [ ] **Step 1** `npm ci --ignore-scripts`(拿到已应用这批 + dsh 闭包的树)。 +- [ ] **每个补丁**:`git mv patches/@deepseek-ai++0.1.2-alpha.3.patch → +0.1.2-alpha.4.patch` + (`cordis-plugin-loader+1.0.3.patch` 名字不含 alpha,**不改名**,只在 Step 3 确认它仍 apply) + → 拿该包 pristine → `npx patch-package --patch-dir patches` 试应用 + → 仅 blob-index / 行号漂移则重生成(patch-package regen 对 `file:` 失败时用 `git diff --no-index` 并修 header) + → 补丁体变化超出行号:读上游 alpha.4 源码确认意图仍成立;`ui-trajectory`(上游 `TrajectoryView.tsx` +69)、 + `ui-agent-preset`、`dsh-workspace` 重点复核目标代码是否还在 / 被上游原生化 → 代码消失则为去留候选,**上报不自删**。 +- [ ] **Step 2** 相关测试硬编码 patch 路径更新:`test/client-modules-resolution-patch.test.ts`、 + `test/cordis-plugin-loader-patch.test.ts`、`test/directory-picker.test.ts`、`test/branding-patch.test.ts`、 + `test/provider-error-patch.test.ts`(部分)、`test/session-delete-patch.test.ts`(若读 `dsh-workspace` 补丁)。 + `patchPath()` 版本无关——多数无需改,先 grep 确认。 +- [ ] **Step 3** 全部完成后 `rm -rf node_modules && npm ci` —— 这 10 个 + dsh 闭包干净应用无警告,剩余 error 恰为 5b 的 8 个。 +- [ ] **Step 4** Commit(各 `fix(patch): 适配 alpha.4`)。 + +--- + +## Task 5b-1: `dsh-api-session-controller` + `dsh-client-ui-chat` + +`dsh-api-session-controller`(**最大补丁**:provider 错误 JSON-safe projection 剥离 + f13001b 的删除 RPC, ++1084 行);`dsh-client-ui-chat`(provider QUOTA/FORBIDDEN 分类 + i18n,3 hunk)。 + +- [ ] `git mv` 两个补丁。拿 pristine alpha.4 `lib/*.js`。 +- [ ] **api-session-controller** 分两块重放: + - provider 块:`const { projections: _projections, ...remoteSummary } = this.listState.summaryFor(session)` + + `ctx.emit("api-session/added", remoteSummary)`(alpha.4 pristine 是否仍发原始 summary?会话 branding 重构 + 是否改了 `summaryFor` 形状?) + - 删除 RPC 块:跟随 alpha.4 的 controller 结构 + 会话 branding(`SessionSeq` 等),释放 Agent / 拒绝子 Agent 逻辑保留 + - 去留:alpha.4 是否原生提供 session 删除 RPC?若是则该块交还上游。 +- [ ] **ui-chat**:去留(alpha.4 是否原生 QUOTA/FORBIDDEN 分类?仍只 AUTH 则保留)→ 重放 → i18n key / zh·en 文案逐字。 +- [ ] 测试全绿:`test/provider-error-patch.test.ts`、`test/session-create-remote-event-patch.test.ts`、 + `test/session-delete-patch.test.ts`(删除 RPC 部分)。上游 API 真改名才动测试。 +- [ ] Commit(各一)。 + +--- + +## Task 5b-2: `dsh-client-ui-workspace`(unread + Finder + 永久删除 UI) + +f13001b 后此补丁承载:未读标记(27 hunk)+ Open in Finder + 删除确认弹窗/错误展示。上游 alpha.4 +`Rows.module.css` + `WorkspaceBrowser.module.css` 都有 diff,CSS hash 会再迁移(alpha.3 为 `_zvJpq_`)。 + +- [ ] `git mv` → grep 上游确认 unread / 删除确认 UI 无原生(`grep -ri "unread\|markSession\|confirmDelete\|工作区文件" /packages/client/ui-workspace/src/`) +- [ ] 取 alpha.4 新 CSS hash,重取 base CSS 串 +- [ ] 重放全部 hunk:unread(store / SessionTree·FlatList 参数 / 行 prop / 右键菜单 / i18n `标为未读`·`标为已读`) + + Finder + 删除确认(工作区文件保留 / 不可撤销文案,错误展示) +- [ ] 去留:分类逐块判,任何被上游原生化的删补丁并记录 +- [ ] 测试全绿:`test/workspace-unread-patch.test.ts`、`test/query-rail-patch.test.ts`、 + `test/open-in-finder.test.ts`(其断言的 workspace patch 名 `+0.1.2-alpha.3.patch` → `+0.1.2-alpha.4.patch`)、 + `test/session-delete-patch.test.ts`(UI 部分) +- [ ] Commit。 + +--- + +## Task 5b-3: `dsh-client-ui-model-selection`(可搜索模型列表,CSS hash) + +- [ ] `git mv` → 确认「可搜索模型列表」上游仍无原生 → 取 alpha.4 新 hash(alpha.3 为 `Ydf5zq_`)→ 机械 hash swap + 行号,13 hunk 逻辑不变 → 生成 → 应用无错。 +- [ ] `test/model-selection-search-patch.test.ts` PASS(`filterModelGroups`、`IconSearchOutline16`、zh/en 文案 `搜索模型或服务商` 等逐字)。 +- [ ] Commit。 + +--- + +## Task 5b-4: `dsh-client-ui-settings-models`(ProviderPicker,双 CSS hash) + +- [ ] `git mv` → 去留复核(`submitLabelKey` alpha.3 已原生,alpha.4 确认仍是;ProviderPicker 卡片网格 / onboarding / 目录搜索 / 图片输入 / 推理等级 / sticky footer 是否原生)→ 取 alpha.4 两个 module hash(alpha.3 为 `A_4Mua_` / `_2CJT8a_`)→ 重放 35 hunk → 硬编码 `dshProvider*` 类不变 → 生成 → 应用无错。 +- [ ] `test/model-picker-patch.test.ts`、`test/model-reasoning-efforts-patch.test.ts`、`test/model-settings-catalog-ux-patch.test.ts`、`test/onboarding-patch.test.ts` 全绿;hash 断言(如 `A_4Mua_customizedBody`)跟随更新。 +- [ ] Commit。 + +--- + +## Task 5b-5: `dsh-client-ui-deliverables` + `dsh-session-persistence` + `dsh-session-persistence-jsonl` 收尾 + +`dsh-client-ui-deliverables`(alpha.3 是机械补丁,alpha.4 现 apply 失败——bundle 漂移); +两个 session-persistence 补丁(f13001b 的永久删除 backend,**正落在 alpha.4 会话 seq/offset branding 重构区**)。 + +- [ ] **deliverables**:`git mv` → 读旧补丁意图 → 对 pristine alpha.4 重放(去留:能力是否原生化)→ 生成 → 应用无错。 +- [ ] **session-persistence** / **-jsonl**:`git mv` → 对 pristine alpha.4 `lib/index.js` 重放 + `PersistenceCoordinator.delete()` / `assertDeletable()` / backend `deleteStored`。**重点**: + alpha.4 的 `SessionPreparations` / `PersistenceCoordinator` 因 `SessionSeq`/`SessionLogOffset` branding + 可能改了方法签名 / `phase` 枚举 / cursor 类型——跟随调整,删除逻辑语义保持。 + 去留:alpha.4 是否已原生提供 `delete` / `deleteStored`?若是则交还上游 + 记录。 +- [ ] `rm -rf node_modules && npm ci` —— **全部 19 补丁干净应用,0 error**。 +- [ ] 测试全绿:`test/session-delete-patch.test.ts`(全部)+ 上游 persistence 相关(`test/*persist*`、`test/*session*` 若有)。 +- [ ] Commit(各一)。 + +--- + +## Task 6: install-brand-assets.mjs 复核 + +- [ ] 解包 `dsh-web-frontend` alpha.4 tarball,比对 `index.html` favicon link / `manifest.webmanifest` icon entry 结构与 `scripts/install-brand-assets.mjs` 的匹配逻辑。 +- [ ] `node scripts/install-brand-assets.mjs`(在已 `npm ci` 的树上)—— 成功输出 brand assets,或响亮失败。 +- [ ] 若匹配落空按「标签 / JSON 键」修,保持目标缺失时抛错。 +- [ ] `npx vitest run test/branding-patch.test.ts` → PASS。 +- [ ] 无 diff 则无 commit(记录「验证无需改动」);有则 Commit。 + +--- + +## Task 7: 主进程 + 移动端桥接 + 分页(重点) + +**Files(按需):** `src/main/runtime/harness-runtime.ts`, `src/main/mobile/lan-mobile-bridge.ts`, `src/main/window-navigation.ts`, 相关 `test/` + +- [ ] **Step 1** 基线:`npx vitest run test/lan-mobile-tunnel.test.ts test/lan-mobile-bridge.test.ts test/lan-mobile-pages.test.ts test/session-create-remote-event-patch.test.ts test/query-rail-patch.test.ts test/profile-compatibility.test.ts test/gpu-fallback.test.ts`。 +- [ ] **Step 2** upstream diff alpha.3→alpha.4: +```bash +cd /Users/alex/Documents/Code/AI/deepseek-harness +git diff dsh-v0.1.2-alpha.3 dsh-v0.1.2-alpha.4 -- packages/client/connection/src packages/api/session-controller/src packages/dsh-web-app/src | grep -E '^\+|^-' | grep -iE 'token|web:|--host|--trusted-host|remote.mux|events.mux|session/page|throughSeq|fromSeq|\$events|clientId|SessionSeq|SessionLogOffset|cursor' | head -80 +``` +- [ ] **Step 3** 逐一确认(会话 branding 重构不应泄漏到 wire): + - `dsh web:` 令牌输出行格式 + - headless stderr 就绪 / 错误归类 + - `/api//` 端点全集、`args` 包裹 + - **`session/page` 分页**:`throughSeq` / `fromSeq` 在 wire 上是否仍为普通 number;游标语义有无变化 + - `/api/remote.mux` 流名、`$events` / `workspace/follow` 订阅、`$events/result` 应答 `clientId` + - `--trusted-host` +- [ ] **Step 4** 有行为变化则最小改 `src/main/**` + 对应测试;仅版本注释漂移则不动(alpha.3 已确认「Since 0.1.2-alpha.1」注释是准确历史记述)。 +- [ ] **Step 5** `npx vitest run` 上述测试全套 → PASS。`npm run typecheck` → 0。 +- [ ] **Step 6** Commit(`fix(main): …` 或 `no commit — nothing to change`)。 +- [ ] **Step 7** 报告里明确写:会话分页 / 桥接 wire 是否受 branding 重构影响的结论 —— Task 9 实机分页冒烟据此重点验。 + +--- + +## Task 8: 版本字样清理 + 全量验证 + +- [ ] `packages/dsh-desktop-preset-transfer/index.js` `PRESET_SOURCE_DSH_VERSION` `'0.1.2-alpha.3'` → `'0.1.2-alpha.4'`。 +- [ ] `test/preset-transfer-patch.test.ts` 3 处运行时 `'0.1.2-alpha.3'` → `'0.1.2-alpha.4'`(行 31 历史注释不动)。 +- [ ] `test/profile-compatibility.test.ts` `version` / `expectedVersion` `'0.1.2-alpha.3'` → `'0.1.2-alpha.4'`。 +- [ ] 6 份 `README*.md`:`@deepseek-ai/dsh@0.1.2-alpha.3` → `@0.1.2-alpha.4`(grep 每份确认)。`test/readme-parity.test.ts` `requiredFacts` token 跟随。 +- [ ] 最终 sweep:`grep -rn "0.1.2-alpha.3" --include=*.ts --include=*.js --include=*.json --include=*.md --include=*.mjs . | grep -vE "node_modules|packages/harness-0.1.2-alpha.4|docs/harness-0.1.2-(upgrade|alpha.3-upgrade).md|.superpowers|docs/superpowers|package-lock"` —— 剩余全为历史注释,列进报告。 +- [ ] `pwd`(worktree)→ `rm -rf node_modules package-lock.json && npm install` → `grep -c harness-0.1.2-alpha.3 package-lock.json` = 0 → `rm -rf node_modules && npm ci`(19 补丁 ✔)。 +- [ ] `npm run typecheck` → 0。`npx vitest run` → 全绿(记录数)。`npm run build` → ok。 +- [ ] Commit `chore: 清理 alpha.3 版本字样,peer 范围对齐 alpha.4`。 + +--- + +## Task 9: 认证链路 + Electron 实机 + 分页冒烟 + +- [ ] `node scripts/verify-harness-auth.mjs` → 令牌 → 401 → 303 → 200。 +- [ ] `pwd`(worktree)`npm run build` → 用项目自身工具启动(`electron-vite preview` 或 `package:dev:dir` 后运行 `dist/` 二进制),cwd = worktree 根,捕获启动日志 ~40s。 +- [ ] 启动日志确认:harness web server 起、**4 个桌面插件全部挂载**、主窗口加载前端、无 `Cannot find package` / `ERR_MODULE_NOT_FOUND` / plugin-recovery。 +- [ ] **会话分页冒烟**(用户要求):新建会话、发一条 prompt、打开历史 / 会话列表翻页,确认不塌、`session/page` 请求 200。移动端桥接路径若可触发一并验。 +- [ ] 失败 → 捕获精确错误,判断是 alpha.4 适配 gap 还是环境问题,报告 DONE_WITH_CONCERNS / BLOCKED,不自行改码。 +- [ ] 无 commit(验证任务)。结果写报告。 + +--- + +## Task 10: 升级文档 + +- [ ] 新建 `docs/harness-0.1.2-alpha.4-upgrade.md`,对齐 alpha.3 文档结构: + - **状态**块(各验证结果 + 数字) + - **本地打包**命令 + 产物计数 + - **上游结构性变更**:会话 seq/offset branding 重构(+ wire 不受影响的结论与证据)、`tool-subagent-report` 删除、`code-runtime-python` 路径移动、ui-* 普遍重建 + - **移动端桥接 / 分页**:Task 7 + Task 9 的验证结论 + - **补丁清单**:新增 / 删除 / 仅偏移 / 实质 rebase 四类表(含各 CSS hash alpha.3→alpha.4 迁移) + - **踩到的问题** + - **剩余工作**(上游发 npm 后:换回语义化版本 + 删本地目录 + registry 重生成 lockfile + 重跑验证) +- [ ] `docs/harness-0.1.2-alpha.3-upgrade.md` 顶部加一行指向 alpha.4 文档。 +- [ ] Commit `docs: harness 0.1.2-alpha.4 升级记录`。 + +--- + +## Task 11: PR #264 更新 + +- [ ] `gh pr edit 264 --title "适配 Harness 0.1.2-alpha.4" --body ...`(正文更新为 alpha.4 的改动摘要 + 验证结果 + 剩余工作;说明本 PR 从 alpha.3 直接推进到 alpha.4,分支名 `harness-alpha3-adapt` 是历史遗留)。 +- [ ] `git push`(分支已 tracking origin)。 + +--- + +## Self-Review + +**Spec 覆盖**:本地打包→T1;依赖+peer+lockfile→T2;闭包补丁→T3;排除清单→T4;19 补丁→T3/T5a/T5b-1..5;install-brand-assets→T6;主进程+桥接+分页→T7;版本清理+全量验证→T8;auth+Electron+分页冒烟→T9;文档→T10;PR→T11。✓ + +**与 alpha.3 计划的差异**:结构性改动小得多(无新增包、只删 1 个已排除包);新增「会话 branding 重构 wire 影响」这条主线,T7/T9 加了分页冒烟;ui-trajectory/ui-workspace 上游有实质改动,T5b-1/5b-2 标注需实质 rebase 而非纯 hash swap。 + +**Placeholder 扫描**:T5 系列是「判定 + 重放」分支而非固定代码——依赖升级任务固有性质,每步给了明确判定标准和命令。 + +**类型一致**:`packages/harness-0.1.2-alpha.4/` 路径、`+0.1.2-alpha.4.patch` 命名、`^0.1.2-alpha.4` peer 全文一致。 diff --git a/docs/superpowers/specs/2026-08-31-harness-0.1.2-alpha.3-upgrade-design.md b/docs/superpowers/specs/2026-08-31-harness-0.1.2-alpha.3-upgrade-design.md new file mode 100644 index 000000000..6302adcb4 --- /dev/null +++ b/docs/superpowers/specs/2026-08-31-harness-0.1.2-alpha.3-upgrade-design.md @@ -0,0 +1,139 @@ +# 设计:升级到 Harness 0.1.2-alpha.3 + +分支:`claude/harness-alpha3-adapt-adfd61`(已存在的 worktree) +基线:当前仓库锁定 `dsh-v0.1.2-alpha.1`(`docs/harness-0.1.2-upgrade.md`) +目标:适配上游 [`dsh-v0.1.2-alpha.3`](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.2-alpha.3),跳过 alpha.2,直接对比 alpha.1 → alpha.3 的净变更。 + +## 约束与前提 + +- 上游仍未发布到 npm registry,继续用仓库内 `file:` tarball 桥接。 +- 上游构建在本地已有克隆 `/Users/alex/Documents/Code/AI/deepseek-harness` 里跑 + (已 `git fetch` 到 alpha.3 标签;`packageManager` 要求 `pnpm@11.7.0`,走 `corepack`)。 +- 交付到「完整适配并验证」:`npm ci` + `build` + `test` + `tsc` + + `verify-harness-auth.mjs` + 实际启动 Electron 全部通过才算完成。 +- 沿用 alpha.1 升级的判断标准:能力「上游是否已原生提供」,是则交还上游删补丁, + 而不是「补丁能不能修好」。 + +## 已确认的上游结构性变更(alpha.1 → alpha.3) + +### 删除的包 + +| 上游包 | dsh-desktop 现状 | 处置 | +| --- | --- | --- | +| `@deepseek-ai/dsh-session-persistence-sqlite` | 未直接依赖;在 `test/release.test.ts` 的 `excludedHarnessPackages` 里 | 包已从上游消失,排除项自然继续成立。清理该行(连同复核 `dsh-storage-sqlite` 是否仍需列出)。**不影响桌面端会话可见性**——桌面端一直用 JSONL 权威存储,SQLite 仅曾作为可选后端,从未打包。 | +| `@deepseek-ai/dsh-agent-spine-demo`(`packages/examples` 整组删除) | `package.json:60` 直接依赖,代码中无其他引用 | 上游把其配置行内联进 `dsh-sdk-minimal`。移除该依赖行;复核它是否在 `@deepseek-ai/dsh` 运行时闭包 / `patches/@deepseek-ai+dsh` 里,若在则同步移除。 | + +### 新增的包 + +`packages/client/ui-schedule`、`packages/util/deque`、`packages/util/time`、 +`packages/util/values`。实施阶段按 `@deepseek-ai/dsh` 与 `dsh-web-app` 的依赖闭包 +判断哪些需要进 `package.json` 和 dsh 闭包补丁(参照 alpha.1 的 +`test/desktop-plugin-closure.test.ts` 不变量)。 + +### 会话持久化(alpha.3 breaking change,但对桌面端近乎零影响) + +上游 `refactor(session)!: remove SQLite persistence backend`: +- 删除的是**权威存储后端** `session-persistence-sqlite`(JSONL 的可选替代品)。 +- 保留 `session-query-sqlite`(可丢弃的 FTS5 搜索索引,从 JSONL 重建)与 + `dsh-storage-sqlite`(通用 domain-KV)。 +- 桌面端 profile 用 `dsh-session-persistence-jsonl` 作为唯一权威存储,历史会话 + 全部照常可见,无需迁移或导出。 +- 「请用旧版本导出」仅针对手动把后端配成 sqlite 的上游 CLI 用户,与桌面端无关。 + +## 实施步骤 + +### 1. 本地打包桥接 + +在 `/Users/alex/Documents/Code/AI/deepseek-harness`: + +```bash +git checkout dsh-v0.1.2-alpha.3 +corepack pnpm install --frozen-lockfile +corepack pnpm run build:official +corepack pnpm exec tsx scripts/release/pack.ts --family vendor --out dist/npm-vendor --concurrency 8 +corepack pnpm exec tsx scripts/release/pack.ts --family dsh --out dist/npm-dsh --concurrency 8 +``` + +产物复制进新目录 `packages/harness-0.1.2-alpha.3/{npm-vendor,npm-dsh}/`, +附一份 `README.md` 说明来源与命令(照抄 alpha.1 的结构)。 +删除 `packages/harness-0.1.2-alpha.1/`。 + +> vendor tarball(cordis / cosmokit / schemastery 等)若版本号未变,仍要重新打包并更新路径。 + +### 2. 依赖切换 + +- `package.json` 中所有 `harness-0.1.2-alpha.1/...-0.1.2-alpha.1.tgz` + → `harness-0.1.2-alpha.3/...-0.1.2-alpha.3.tgz`(含 vendor 段实际版本号)。 +- 删除 `@deepseek-ai/dsh-agent-spine-demo` 行。 +- 按闭包结论补入需要的新增 `util/*` 包。 +- `packages/dsh-desktop-market-installer` peer 范围 → `^0.1.2-alpha.3`。 +- 重新生成 `package-lock.json`(`npm install`,来源为本地 tarball)。 + +### 3. Patch 适配(16 个) + +逐个把 `patches/*+0.1.2-alpha.1.patch` 重建到 alpha.3 构建产物并改名为 +`+0.1.2-alpha.3.patch`。对每个补丁判定: + +- **仅行号 / CSS hash 偏移**:重新生成 diff。 +- **上游 API 改名 / 结构变更**:跟随调整(参照 alpha.1 记录的 `ctx.uiWorkspace`、 + `submitLabelKey*`、`useSessionPendingInteraction` 等模式,alpha.3 可能再动)。 +- **上游已原生提供**:删补丁,在升级文档记录「交还上游」。 +- `@deepseek-ai+dsh` 闭包补丁:同步 spine-demo 移除、util 新增包的 manifest 注入, + 更新行号。 +- `dsh-api-session-controller` 补丁:复核会话分页 / 模型目录接口在 alpha.3 是否再变。 + +产出新的补丁清单表(新增 / 删除 / 仅偏移三类)写进升级文档。 + +### 4. 宿主侧脚本与桌面插件 + +- `scripts/install-brand-assets.mjs`:复核对 alpha.3 `dsh-web-frontend` 前端资源的 + 匹配是否仍命中(favicon href、manifest icon purpose、index.html 结构);不命中则 + 按 alpha.1 的「按标签 / JSON 键」策略修,且目标缺失仍要响亮失败。 +- `harness-runtime`(`src/main/**`):令牌捕获(`dsh web:` 输出行)、headless 进度 + stderr 就绪探测、`--trusted-host` / `--host 127.0.0.1` 行为按 alpha.3 复核。 +- `packages/dsh-desktop-*`(client-ui / preset-transfer / market-installer / + hmr-fallback):随上游 slot / Fetch 路由接缝复核,必要时跟随调整。 + +### 5. 验证(顺序执行,全绿才算完成) + +1. `npm ci`(含 postinstall;**不得**加 `--ignore-scripts`)→ 0 +2. `npm run build` → ok +3. `npm test` → 记录通过数与已知 teardown 退出码 +4. `npm run typecheck`(`tsc --noEmit`)→ 0 +5. `node scripts/verify-harness-auth.mjs` → 401 → 303 → 200 +6. 实际启动 Electron → profile 四个桌面插件全部挂载 +7. 手机桥接 / 隧道路径(`test/lan-mobile-tunnel.test.ts` 等)回归 + +任一步失败进入 systematic-debugging,不带病继续。 + +### 6. 文档与知识 + +- 新增 `docs/harness-0.1.2-alpha.3-upgrade.md`:状态、本地打包命令、上游结构性变更、 + 主机 API / 桥接是否再变、补丁清单增减、踩到的启动期问题、剩余工作 + (上游发布 npm 后换回语义化版本)。 +- 更新 `docs/harness-0.1.2-upgrade.md` 顶部指向新文档(或标注为历史)。 +- 更新根及各语言 README 里出现的 `0.1.2-alpha.1` 版本字样。 +- 复核 `test/*` 中硬编码 `0.1.2-alpha.1` / `harness-0.1.2-alpha.1` 路径的用例 + (`release.test.ts`、`version-info.test.ts`、`desktop-plugin-closure.test.ts` 等)。 +- 需要时更新 memory(`renderer-hot-path-constraints` 等若涉及桥接接口变更)。 + +## 交付物 + +本分支上的一组提交:`packages/harness-0.1.2-alpha.3/` 打包产物、`package.json` / +lockfile、重命名并适配后的 `patches/`、脚本改动、`docs/harness-0.1.2-alpha.3-upgrade.md`、 +测试改动。验证结果如实记录在升级文档中(失败也照记)。 + +## 非目标 + +- 不等上游发 npm、不在本次换回语义化版本(列入「剩余工作」)。 +- 不做与升级无关的重构。 +- 不引入上游未启用的 provider / 测试包(e2b、lsp、mock-server 等保持排除)。 + +## 风险 + +- **上游构建耗时 / 依赖体积大**:`pnpm install` + `build:official` 可能较慢,磁盘占用高。 +- **alpha.2 中间态被跳过**:若某改名分两步完成,直接 diff 可能漏掉过渡说明;以最终 + 构建产物为准,diff 仅作线索。 +- **补丁漂移**:CSS module hash 每次构建都可能变,需以 alpha.3 实际产物为准逐一重取。 +- **闭包不变量**:新增 `util/*` 包若进了 `@deepseek-ai/dsh` 闭包却漏注入 manifest, + 会整棵插件树失败(alpha.1 已踩过)。 diff --git a/docs/superpowers/specs/2026-09-01-harness-0.1.2-alpha.4-upgrade-design.md b/docs/superpowers/specs/2026-09-01-harness-0.1.2-alpha.4-upgrade-design.md new file mode 100644 index 000000000..cac63ae35 --- /dev/null +++ b/docs/superpowers/specs/2026-09-01-harness-0.1.2-alpha.4-upgrade-design.md @@ -0,0 +1,131 @@ +# 设计:升级到 Harness 0.1.2-alpha.4 + +分支:`claude/harness-alpha3-adapt-adfd61`(复用;PR #264 改为 alpha.4,跳过 alpha.3 单独合并) +基线:本分支已完成 alpha.3 适配(`docs/harness-0.1.2-alpha.3-upgrade.md`,commit `bcbf0fb`) +目标:从 alpha.3 直接推进到上游 [`dsh-v0.1.2-alpha.4`](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.2-alpha.4)(commit `4e84901`,比 alpha.3 多 297 提交)。 + +## 约束与前提 + +- 上游仍未发布 npm,继续用 `packages/harness-/` 内 `file:` tarball 桥接。 +- 上游构建在本地 `/Users/alex/Documents/Code/AI/deepseek-harness`(已 fetch alpha.4 标签)。 +- 交付到「完整适配并验证」:`npm ci` + `build` + `test` + `tsc` + `verify-harness-auth.mjs` + Electron 实机全绿。 +- 补丁去留标准不变:「上游是否已原生提供」。 +- **用户特别要求**:重点验证移动端桥接(`lan-mobile-bridge.ts`)与 `session/page` 分页对会话破坏性变更的适配。 + +## 上游结构性变更(alpha.3 → alpha.4) + +### 会话破坏性变更(本次最需关注) + +`refactor(session)!: distinguish event seqs from log offsets`(commit `27bf103`): +`@deepseek-ai/dsh-brand` 新增 `BrandedNumber` / `brandNumber()`;`dsh-session` 拥有 +`SessionSeq`(指已存在事件)与 `SessionLogOffset`(指日志间隙/前缀长度/读取偏移)两个 brand, +`SessionSeqCursor = SessionSeq | -1`、`OptionalSessionSeq = SessionSeq | null`。 +`SessionHeader.seedLength` 被 `isSeeded: boolean` + 旁路 `inheritedEventCount` 取代。 + +**关键:这是编译期 TS branding 重构。**上游 Agent Note 明确: +> API、SDK、DeepSeek、telemetry、query row 与 JSON 表示继续携带普通 number; +> 磁盘 v0 格式与公共数值 wire 不变。 + +因此移动端桥接走的 HTTP `/api/session/page`(`throughSeq` 为普通 number)与 v0 JSONL +理论上不受影响。但 `dsh-api-session-controller` 的 `lib/index.js` 内部代码形状会变 +(补丁 context 行漂移),且 `test/session-create-remote-event-patch.test.ts` 需复核。 +**验证阶段以实机分页 + 桥接冒烟为准,不只靠单测。** + +### 被删除 / 移动的包 + +| 上游变更 | dsh-desktop 现状 | 处置 | +| --- | --- | --- | +| `@deepseek-ai/dsh-tool-subagent-report` 删除 | 在 `test/release.test.ts` `excludedHarnessPackages` | 排除断言变 vacuous —— 清理该行 + 复核是否仍为传递依赖 | +| `code-runtime-python` 从 `packages/code-runtime/` 移到 `packages/experimental/` | 已排除,包名 `@deepseek-ai/dsh-code-runtime-python` 未变 | 无影响,仅确认 tarball 名不变 | + +包总数 257 → 256(净 −1)。**无 dsh-desktop 需要新增的包。** + +### ui-* 包普遍重构 + +`ui-workspace` / `ui-trajectory` / `ui-settings-models` / `ui-model-selection` / +`ui-agent-preset` / `ui-chat` 全部重新构建(`147 files changed` in patch-relevant packages)。 +CSS module hash 会再次变化;bundle 结构可能漂移。ui-trajectory / ui-workspace 还删除了 +`src/invariant.ts`(内部清理,不影响补丁意图)。 + +## 实施步骤 + +与 alpha.3 升级同形状,但结构性改动更小。 + +### 1. 本地打包 +`git checkout dsh-v0.1.2-alpha.4` → `corepack pnpm install --frozen-lockfile` → +`build:official` → `pack.ts --family vendor|dsh`。产物进新目录 +`packages/harness-0.1.2-alpha.4/{npm-vendor,npm-dsh}/` + README。删除 +`packages/harness-0.1.2-alpha.3/`。 + +### 2. 依赖切换 +`package.json` 所有 `harness-0.1.2-alpha.3/...-0.1.2-alpha.3.tgz` → +`harness-0.1.2-alpha.4/...-0.1.2-alpha.4.tgz`(含 vendor 段实际版本号,逐一核对 tarball 文件名)。 +3 个桌面插件包(client-ui / preset-transfer / market-installer)的 `@deepseek-ai/dsh*` peer +`^0.1.2-alpha.3` → `^0.1.2-alpha.4`。重生成 `package-lock.json`(`npm install`), +验证 lockfile 无 `harness-0.1.2-alpha.3` 残留、无 registry-resolved `@deepseek-ai/dsh-*`。 + +### 3. 补丁适配(16 个) +逐个 `git mv +0.1.2-alpha.3.patch → +0.1.2-alpha.4.patch`(cordis-plugin-loader 跟随其 +vendor 版本号),对着 alpha.4 构建产物重建: +- **闭包补丁 `@deepseek-ai+dsh`**:注入的 4 个 `dsh-desktop-*` 行不变,仅 manifest 行号 / + blob-index 更新;确认 alpha.4 未从 dsh 依赖里删/加影响闭包的包。 +- **8 个机械补丁**(cordis-plugin-loader / dsh-client-modules / ui-deliverables / + ui-directory-picker-native / ui-layout / ui-sidebar / llm-deepseek / llm-pi-ai): + 多为改名 + 行号偏移;directory-picker / sidebar / layout 复核补丁目标代码仍存在。 +- **provider 错误组 3 个**(dsh-api-session-controller / ui-chat / ui-trajectory): + ui-trajectory 有较大重构(`TrajectoryView.tsx` +69 行、`views.client.spec.tsx` +178), + 需重点复核 provider 错误提示的注入点是否还在、是否被上游原生化。 + dsh-api-session-controller 受会话 branding 重构影响,`summaryFor` / `api-session/added` + 区域 context 行会漂移。 +- **CSS-hash 组 4 个**(ui-model-selection / ui-workspace / ui-settings-models / + ui-agent-preset):alpha.4 重建 → 每个 hash 再次迁移。ui-workspace 的 + `Rows.module.css` + `WorkspaceBrowser.module.css` 都有 diff,unread 补丁的 base 串要重取。 +去留判定标准不变,任何「上游已原生」的能力删补丁并记录。 + +### 4. sqlite/subagent-report 排除项 +`test/release.test.ts`:`dsh-tool-subagent-report` 排除行随上游删包处理(删除或加注释); +复核 `dsh-session-persistence-sqlite`(alpha.3 已删的注释)与 `dsh-storage-sqlite`。 + +### 5. 宿主脚本 / 主进程 / 移动端桥接(重点) +- `install-brand-assets.mjs`:复核对 alpha.4 `dsh-web-frontend` 的匹配。 +- `harness-runtime.ts`:令牌捕获 / stderr 就绪 / `CORE_BUNDLES` 复核(alpha.3 已确认无变, + alpha.4 再比一次 upstream diff)。 +- **`lan-mobile-bridge.ts` + `session/page` 分页**:对着 alpha.4 的 `dsh-client-connection` + 与 `dsh-api-session-controller` 构建产物逐一比对 —— RPC 信封、`/api//` 端点、 + `throughSeq` 语义、`$events` / `workspace/follow` 流、`$events/result` 应答。 + `session/page` 的游标是否仍为普通 number?会话 seq/offset 区分是否泄漏到 wire? + 跑 `test/lan-mobile-*` 全套 + `test/session-create-remote-event-patch.test.ts` + + `test/query-rail-patch.test.ts` + `test/profile-compatibility.test.ts`。 + **验证阶段实机建一个会话、发 prompt、翻历史分页确认不塌。** + +### 6. 版本字样清理 +`PRESET_SOURCE_DSH_VERSION` `'0.1.2-alpha.3'` → `'0.1.2-alpha.4'` + 对应 3 处测试 fixture; +`test/profile-compatibility.test.ts` 的 `version` / `expectedVersion`;6 份 README 的 +`@deepseek-ai/dsh@0.1.2-alpha.3` → `@0.1.2-alpha.4`;`test/readme-parity.test.ts` token。 +`src/main/**` / `scripts/**` 的「Since 0.1.2-alpha.1 …」历史注释**不动**。 + +### 7. 验证(顺序,全绿才算完成) +`npm ci`(含 postinstall,16 补丁 ✔)→ `npm run build` → `npm test` → `npm run typecheck` +→ `node scripts/verify-harness-auth.mjs` → Electron 实机(profile 4 插件挂载 + 会话分页冒烟)。 + +### 8. 文档 +新增 `docs/harness-0.1.2-alpha.4-upgrade.md`(对齐 alpha.3 文档结构,记录会话 branding +重构的 wire 影响结论、补丁清单增减、CSS hash 迁移表、移动端桥接验证结果); +`docs/harness-0.1.2-alpha.3-upgrade.md` 顶部加 alpha.4 交叉引用。 + +## 交付物 +本分支上一组提交(打包产物、依赖、lockfile、重命名并适配后的 patches、脚本/主进程、 +测试、`docs/harness-0.1.2-alpha.4-upgrade.md`)。PR #264 标题 / 正文更新为 alpha.4。 + +## 非目标 +- 不等上游发 npm、不换回语义化版本(继续列「剩余工作」)。 +- 不做与升级无关的重构。 +- 不重新引入排除的 provider / 测试包。 + +## 风险 +- **会话 branding 重构泄漏到 wire**:Agent Note 说不会,但 `dsh-api-session-controller` + 补丁与移动端分页要以实测为准。 +- **ui-trajectory / ui-workspace 大改**:补丁可能需要实质 rebase 而非机械 hash swap, + 甚至部分能力被上游原生化。 +- 补丁上下文含构建机绝对路径的 4 个 CSS 补丁(alpha.3 已记录)——换机器重打包仍需重生成。 +- 子代理易误入主检出(alpha.3 踩过两次)——每个实施 brief 必须强化「只在 worktree 内操作」。 diff --git a/package-lock.json b/package-lock.json index 402c50c40..071e3b1b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,233 +10,237 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@deepseek-ai/cordis": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-4.0.1.tgz", - "@deepseek-ai/cordis-plugin-group": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-group-1.0.1.tgz", - "@deepseek-ai/cordis-plugin-hmr": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-hmr-1.0.16.tgz", - "@deepseek-ai/cordis-plugin-include": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-include-1.0.6.tgz", - "@deepseek-ai/cordis-plugin-loader": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-loader-1.0.2.tgz", - "@deepseek-ai/cordis-plugin-timer": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-timer-1.1.3.tgz", - "@deepseek-ai/cosmokit": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cosmokit-1.8.2.tgz", - "@deepseek-ai/dsh": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-acp": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-acp-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-acp-app": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-acp-app-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-default-model": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-default-model-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-instructions": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-instructions-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-loop": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-loop-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-presets": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-presets-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-spine-demo": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-spine-demo-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-tool-presentation": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-tool-presentation-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-anonymous-user-id": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-anonymous-user-id-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-api-gateway": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-gateway-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-api-remotes": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-remotes-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-api-session-controller": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-session-controller-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-api-settings-controller": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-settings-controller-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-api-workspace-controller": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-workspace-controller-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-app-boot": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-app-boot-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-atomic-write": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-atomic-write-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-attachment": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-attachment-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-attachment-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-attachment-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-authorization": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-authorization-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-base": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-base-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-bash-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-bash-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-bash-sandbox": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-bash-sandbox-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-brand": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-brand-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-connection": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-connection-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-hmr": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-hmr-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-locale": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-locale-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-modules": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-modules-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-store": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-store-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-agent-preset": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-agent-preset-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-approval": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-approval-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-attachment": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-attachment-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-brand-official": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-brand-official-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-chat": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-chat-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-commands": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-commands-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-conversation": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-conversation-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-cordis": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-cordis-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-deliverables": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-deliverables-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-directory-picker-browse": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-browse-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-directory-picker-native": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-native-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-goal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-goal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-input-trigger": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-input-trigger-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-jobs": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-jobs-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-layout": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-layout-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-message-feedback": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-message-feedback-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-model-selection": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-model-selection-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-permission-presets": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-permission-presets-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-plan": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-plan-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-primitives": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-primitives-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-reference": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-reference-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-renderer": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-renderer-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-session": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-session-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-settings": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-settings-general": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-general-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-settings-models": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-models-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-settings-plugin-inventory": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugin-inventory-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-settings-plugins": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugins-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-sidebar": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-sidebar-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-skill": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-skill-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-slots": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-slots-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-subagent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-subagent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-theme": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-theme-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-tool": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-tool-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-trajectory": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-trajectory-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-user-questions": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-user-questions-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-workflow-run": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-workflow-run-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-workspace": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-workspace-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-cmdline": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-cmdline-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-code-runtime": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-code-runtime-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-code-runtime-worker-thread": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-code-runtime-worker-thread-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-command-compact": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-command-compact-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-command-feedback": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-command-feedback-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-command-goal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-command-goal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-commands": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-commands-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-compaction": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-compaction-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-compaction-basic": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-compaction-basic-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-compaction-tool-result-pruner": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-compaction-tool-result-pruner-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-cordis-client-runner": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-cordis-client-runner-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-cordis-host-runner": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-cordis-host-runner-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-credentials": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-credentials-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-credentials-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-credentials-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-deepseek-llm-api-extensions": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-deepseek-llm-api-extensions-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-file-reference": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-file-reference-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-file-reference-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-file-reference-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-fs": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-fs-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-fs-observation-policy": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-observation-policy-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-fs-sandbox": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-sandbox-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-goal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-goal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-goal-round-driver": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-goal-round-driver-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-headless": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-headless-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-home-paths": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-home-paths-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-hook-protocol": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-hook-protocol-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-hooks-claude-code": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-hooks-claude-code-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-hooks-codex": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-hooks-codex-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-directory-picker": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-directory-picker-auto": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-auto-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-directory-picker-browse": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-browse-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-directory-picker-native": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-native-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-frontend-static": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-frontend-static-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-plugin-inventory": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-plugin-inventory-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-webserver": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-webserver-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-invariants": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-invariants-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-jobs": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-jobs-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-jobs-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-jobs-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-launch-environment": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-launch-environment-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-llm": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-llm-deepseek": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-deepseek-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-llm-pi-ai": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-pi-ai-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-llm-retry": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-retry-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-mcp-client": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-mcp-client-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-message-feedback": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-message-feedback-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-native-command": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-native-command-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-output-retention": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-output-retention-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-permission-presets": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-permission-presets-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-persona": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-persona-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-plan-mode": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-plan-mode-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-plugin-package-inventory-deepseek": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-plugin-package-inventory-deepseek-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-pwsh-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-pwsh-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-pwsh-sandbox": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-pwsh-sandbox-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-repeat-tool-reminder": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-repeat-tool-reminder-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sandbox": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sandbox-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sandbox-policy": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-policy-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sandbox-windows-acl": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-windows-acl-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-schedule": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-schedule-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-scope": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-scope-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sdk-app": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-app-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sdk-jsonrpc-server": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-jsonrpc-server-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sdk-minimal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-minimal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sdk-protocol": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-protocol-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-checkpoint-policy": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-checkpoint-policy-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-log-deepseek": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-log-deepseek-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-log-export": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-log-export-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-persistence": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-persistence-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-persistence-jsonl": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-persistence-jsonl-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-projection": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-projection-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-projection-cache": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-projection-cache-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-query": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-query-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-query-sqlite": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-query-sqlite-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-reference": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-reference-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-stats": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-stats-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-telemetry": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-telemetry-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-telemetry-otel": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-telemetry-otel-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-title": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-title-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-title-first-prompt-llm": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-title-first-prompt-llm-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-title-llm": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-title-llm-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-settings": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-settings-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-settings-file": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-settings-file-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-shell": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-shell-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-shell-env": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-shell-env-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-skill": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-skill-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-skill-badge": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-skill-badge-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-skill-filesystem": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-skill-filesystem-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-spill": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-spill-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-spill-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-spill-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-spill-policy": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-spill-policy-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-storage": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-storage-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-storage-domain": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-storage-domain-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-storage-json": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-storage-json-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subagent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subagent-fork-in-process": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-fork-in-process-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subagent-in-process-driver": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-in-process-driver-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subagent-spawn-in-process": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-spawn-in-process-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subprocess": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subprocess-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subprocess-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subprocess-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-system-prompt": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-system-prompt-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-terminal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-terminal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-terminal-bash": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-terminal-bash-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-time-context": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-time-context-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-timeout": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-timeout-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tmux-context": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tmux-context-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-token-meter": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-token-meter-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-ask-user": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-ask-user-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-bash": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-bash-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-bash-persistent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-bash-persistent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-call-timeout-policy": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-call-timeout-policy-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-cordis": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-cordis-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-fs": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-fs-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-fs-search": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-fs-search-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-goal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-goal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-jobs": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-jobs-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-pwsh": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-pwsh-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-pwsh-persistent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-pwsh-persistent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-ralph": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-ralph-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-skill": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-skill-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-str-replace-editor": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-str-replace-editor-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-subagent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-subagent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-subagent-control": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-subagent-control-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-subagent-report": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-subagent-report-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-todo": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-todo-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-web": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-web-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-workflow": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-workflow-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tools": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tools-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-typert-generator": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-generator-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-typert-loader": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-loader-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-typert-protocol": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-protocol-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-typert-registry": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-registry-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-user-approval": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-user-approval-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-user-questions": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-user-questions-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-util-crypto": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-util-crypto-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-util-workspace-path": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-util-workspace-path-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-web": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-web-app": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-app-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-web-fetch-http": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-fetch-http-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-web-frontend": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-frontend-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-web-search-deepseek": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-search-deepseek-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-webhook": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-webhook-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-webhook-github": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-webhook-github-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-win32-process": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-win32-process-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-workflow": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-workflow-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-workflow-worker-thread": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-workflow-worker-thread-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-workspace": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-workspace-0.1.2-alpha.1.tgz", - "@deepseek-ai/schemastery": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-schemastery-3.18.1.tgz", + "@deepseek-ai/cordis": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-4.0.2.tgz", + "@deepseek-ai/cordis-plugin-group": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-group-1.0.2.tgz", + "@deepseek-ai/cordis-plugin-hmr": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-hmr-1.0.17.tgz", + "@deepseek-ai/cordis-plugin-include": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-include-1.0.7.tgz", + "@deepseek-ai/cordis-plugin-loader": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-loader-1.0.3.tgz", + "@deepseek-ai/cordis-plugin-timer": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-timer-1.1.4.tgz", + "@deepseek-ai/cosmokit": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cosmokit-1.8.3.tgz", + "@deepseek-ai/dsh": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-acp": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-acp-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-acp-app": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-acp-app-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent-default-model": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-default-model-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent-instructions": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-instructions-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent-loop": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-loop-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent-presets": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-presets-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent-tool-presentation": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-tool-presentation-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-anonymous-user-id": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-anonymous-user-id-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-api-gateway": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-gateway-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-api-remotes": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-remotes-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-api-session-controller": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-session-controller-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-api-settings-controller": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-settings-controller-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-api-workspace-controller": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-workspace-controller-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-app-boot": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-app-boot-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-atomic-write": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-atomic-write-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-attachment": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-attachment-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-attachment-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-attachment-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-authorization": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-authorization-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-base": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-base-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-bash-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-bash-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-bash-sandbox": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-bash-sandbox-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-brand": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-brand-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-connection": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-connection-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-hmr": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-hmr-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-locale": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-locale-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-modules": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-modules-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-store": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-store-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-agent-preset": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-agent-preset-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-approval": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-approval-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-attachment": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-attachment-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-brand-official": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-brand-official-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-chat": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-chat-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-commands": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-commands-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-conversation": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-conversation-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-cordis": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-cordis-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-deliverables": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-deliverables-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-directory-picker-browse": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-browse-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-directory-picker-native": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-native-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-goal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-goal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-input-trigger": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-input-trigger-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-jobs": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-jobs-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-layout": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-layout-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-message-feedback": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-message-feedback-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-model-selection": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-model-selection-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-permission-presets": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-permission-presets-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-plan": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-plan-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-primitives": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-primitives-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-reference": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-reference-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-renderer": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-renderer-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-schedule": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-schedule-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-session": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-session-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-settings": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-settings-general": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-general-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-settings-models": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-models-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-settings-plugin-inventory": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugin-inventory-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-settings-plugins": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugins-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-sidebar": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-sidebar-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-skill": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-skill-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-slots": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-slots-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-subagent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-subagent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-theme": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-theme-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-tool": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-tool-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-trajectory": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-trajectory-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-user-questions": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-user-questions-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-workflow-run": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-workflow-run-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-workspace": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-workspace-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-cmdline": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-cmdline-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-code-runtime": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-code-runtime-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-code-runtime-worker-thread": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-code-runtime-worker-thread-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-command-compact": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-command-compact-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-command-feedback": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-command-feedback-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-command-goal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-command-goal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-commands": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-commands-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-compaction": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-compaction-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-compaction-basic": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-compaction-basic-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-compaction-tool-result-pruner": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-compaction-tool-result-pruner-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-cordis-client-runner": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-cordis-client-runner-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-cordis-host-runner": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-cordis-host-runner-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-credentials": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-credentials-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-credentials-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-credentials-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-deepseek-llm-api-extensions": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-deepseek-llm-api-extensions-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-deque": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-deque-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-file-reference": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-file-reference-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-file-reference-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-file-reference-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-fs": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-fs-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-fs-observation-policy": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-observation-policy-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-fs-sandbox": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-sandbox-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-goal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-goal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-goal-round-driver": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-goal-round-driver-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-headless": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-headless-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-home-paths": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-home-paths-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-hook-protocol": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-hook-protocol-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-hooks-claude-code": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-hooks-claude-code-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-hooks-codex": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-hooks-codex-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-directory-picker": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-directory-picker-auto": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-auto-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-directory-picker-browse": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-browse-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-directory-picker-native": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-native-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-frontend-static": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-frontend-static-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-plugin-inventory": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-plugin-inventory-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-webserver": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-webserver-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-invariants": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-invariants-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-jobs": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-jobs-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-jobs-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-jobs-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-launch-environment": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-launch-environment-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-llm": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-llm-deepseek": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-deepseek-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-llm-pi-ai": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-pi-ai-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-llm-retry": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-retry-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-mcp-client": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-mcp-client-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-message-feedback": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-message-feedback-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-native-command": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-native-command-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-output-retention": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-output-retention-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-permission-presets": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-permission-presets-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-persona": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-persona-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-plan-mode": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-plan-mode-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-plugin-package-inventory-deepseek": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-plugin-package-inventory-deepseek-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-pwsh-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-pwsh-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-pwsh-sandbox": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-pwsh-sandbox-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-repeat-tool-reminder": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-repeat-tool-reminder-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sandbox": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sandbox-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sandbox-policy": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-policy-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sandbox-windows-acl": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-windows-acl-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-schedule": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-schedule-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-scope": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-scope-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sdk-app": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-app-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sdk-jsonrpc-server": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-jsonrpc-server-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sdk-minimal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-minimal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sdk-protocol": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-protocol-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-checkpoint-policy": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-checkpoint-policy-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-log-deepseek": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-log-deepseek-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-log-export": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-log-export-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-persistence": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-persistence-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-persistence-jsonl": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-persistence-jsonl-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-projection": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-projection-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-projection-cache": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-projection-cache-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-query": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-query-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-query-sqlite": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-query-sqlite-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-reference": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-reference-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-stats": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-stats-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-telemetry": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-telemetry-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-telemetry-otel": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-telemetry-otel-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-title": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-title-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-title-first-prompt-llm": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-title-first-prompt-llm-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-title-llm": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-title-llm-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-turn-outline": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-turn-outline-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-settings": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-settings-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-settings-file": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-settings-file-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-shell": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-shell-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-shell-env": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-shell-env-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-skill": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-skill-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-skill-badge": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-skill-badge-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-skill-filesystem": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-skill-filesystem-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-spill": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-spill-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-spill-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-spill-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-spill-policy": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-spill-policy-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-storage": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-storage-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-storage-domain": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-storage-domain-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-storage-json": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-storage-json-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subagent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subagent-fork-in-process": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-fork-in-process-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subagent-in-process-driver": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-in-process-driver-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subagent-spawn-in-process": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-spawn-in-process-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subprocess": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subprocess-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subprocess-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subprocess-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-system-prompt": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-system-prompt-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-terminal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-terminal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-terminal-bash": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-terminal-bash-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-time-context": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-time-context-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-timeout": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-timeout-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tmux-context": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tmux-context-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-token-meter": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-token-meter-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-ask-user": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-ask-user-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-bash": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-bash-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-bash-persistent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-bash-persistent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-call-timeout-policy": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-call-timeout-policy-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-cordis": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-cordis-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-fs": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-fs-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-fs-search": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-fs-search-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-goal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-goal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-jobs": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-jobs-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-pwsh": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-pwsh-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-pwsh-persistent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-pwsh-persistent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-ralph": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-ralph-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-skill": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-skill-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-str-replace-editor": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-str-replace-editor-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-subagent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-subagent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-subagent-control": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-subagent-control-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-todo": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-todo-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-web": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-web-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-workflow": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-workflow-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tools": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tools-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-typert-generator": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-generator-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-typert-loader": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-loader-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-typert-protocol": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-protocol-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-typert-registry": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-registry-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-user-approval": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-user-approval-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-user-questions": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-user-questions-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-util-crypto": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-crypto-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-util-time": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-time-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-util-values": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-values-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-util-workspace-path": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-workspace-path-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-web": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-web-app": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-app-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-web-fetch-http": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-fetch-http-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-web-frontend": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-frontend-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-web-search-deepseek": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-search-deepseek-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-webhook": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-webhook-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-webhook-github": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-webhook-github-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-win32-process": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-win32-process-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-workflow": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-workflow-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-workflow-worker-thread": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-workflow-worker-thread-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-workspace": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-workspace-0.1.2-alpha.4.tgz", + "@deepseek-ai/schemastery": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-schemastery-3.18.2.tgz", "dsh-desktop-client-ui": "file:packages/dsh-desktop-client-ui", "dsh-desktop-hmr-fallback": "file:packages/dsh-desktop-hmr-fallback", "dsh-desktop-market-installer": "file:packages/dsh-desktop-market-installer", "dsh-desktop-preset-transfer": "file:packages/dsh-desktop-preset-transfer", + "dshmarket": "file:packages/dshmarket", "electron-updater": "^6.8.9", "node": "24.9.0", "pnpm": "10.34.5", @@ -267,6 +271,26 @@ "zod": "^3.25.0 || ^4.0.0" } }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.91.1", + "resolved": "https://registry.npmmirror.com/@anthropic-ai/sdk/-/sdk-0.91.1.tgz", + "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmmirror.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", @@ -395,9 +419,9 @@ } }, "node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler": { - "version": "4.11.3", - "resolved": "https://registry.npmmirror.com/@smithy/node-http-handler/-/node-http-handler-4.11.3.tgz", - "integrity": "sha512-2jY1tSpERfPfWqyBV2pH+iGFaghVsIJszJNsT7hxtQYhVJpWDyc0LqOWI+nXOxOAHaEfZ4PXXtp1wW1TGpHhkA==", + "version": "4.12.0", + "resolved": "https://registry.npmmirror.com/@smithy/node-http-handler/-/node-http-handler-4.12.0.tgz", + "integrity": "sha512-0mq1pHadfyXCYCqm2cNpbjNIT+fbaUpNxewZb/YNr2L0IrEVMOb8gM/Fl4K6XvHCW3uSNDFwPl/+iKm0bx9jYg==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.33.3", @@ -450,9 +474,9 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.81", - "resolved": "https://registry.npmmirror.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.81.tgz", - "integrity": "sha512-Rml+WitoFvXmv6JZ18U/xGdGDGGvB/mOin0ya0lTnTrdC0Z1lrVxTYh7iNklZBcvcRMrs4DoEf6xy1KWyrLQQw==", + "version": "3.972.82", + "resolved": "https://registry.npmmirror.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.82.tgz", + "integrity": "sha512-znDkEOGXB8W3kG1LJUKP3foBZY/9qLM0eil/DxWXSp37XsdsRLQHE/d/OaCGGVgKpA6znR38h/+INk8do1FjiA==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.70", @@ -607,9 +631,9 @@ } }, "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler": { - "version": "4.11.3", - "resolved": "https://registry.npmmirror.com/@smithy/node-http-handler/-/node-http-handler-4.11.3.tgz", - "integrity": "sha512-2jY1tSpERfPfWqyBV2pH+iGFaghVsIJszJNsT7hxtQYhVJpWDyc0LqOWI+nXOxOAHaEfZ4PXXtp1wW1TGpHhkA==", + "version": "4.12.0", + "resolved": "https://registry.npmmirror.com/@smithy/node-http-handler/-/node-http-handler-4.12.0.tgz", + "integrity": "sha512-0mq1pHadfyXCYCqm2cNpbjNIT+fbaUpNxewZb/YNr2L0IrEVMOb8gM/Fl4K6XvHCW3uSNDFwPl/+iKm0bx9jYg==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.33.3", @@ -1043,20 +1067,20 @@ } }, "node_modules/@deepseek-ai/cordis": { - "version": "4.0.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-4.0.1.tgz", - "integrity": "sha512-BVd68gD2fAr8XG1HTEgJb8wsPBW5pz/9vcn18nAXIVKyMPBFaSx4RHsSioMwxjU7MYeDFe66tSMc05QqNrPxSw==", + "version": "4.0.2", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-4.0.2.tgz", + "integrity": "sha512-L6xuMRR0JKOThYpKa5FGR3oKVzJC/K/LqZsTc6NFIbp7njxkoR1Gwagsv2GAedW/JIpPAOOFmh/zTWykUlQMlQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/cosmokit": "^1.8.2", + "@deepseek-ai/cosmokit": "^1.8.3", "@standard-schema/spec": "^1.1.0" }, "bin": { "cordis": "bin.js" }, "peerDependencies": { - "@deepseek-ai/cordis-plugin-include": "^1.0.6", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2" + "@deepseek-ai/cordis-plugin-include": "^1.0.7", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3" }, "peerDependenciesMeta": { "@deepseek-ai/cordis-plugin-include": { @@ -1068,56 +1092,56 @@ } }, "node_modules/@deepseek-ai/cordis-plugin-group": { - "version": "1.0.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-group-1.0.1.tgz", - "integrity": "sha512-FY46Ctuk0aLOQcW5WvTKhNVeBYHZzJda5AwJfeiODeJyISC7wo6RKgDU1xvBgdOb6e65uhCopc7DSYymWzLaAw==", + "version": "1.0.2", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-group-1.0.2.tgz", + "integrity": "sha512-rFVxvEJlfKPIXj2DDNENtybIg0uDpDQJ01J6f/mrMsTj5ieWafK3/gKbRP71TMeinrd6WoC9hGwzEI1UMgoTjw==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3" } }, "node_modules/@deepseek-ai/cordis-plugin-hmr": { - "version": "1.0.16", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-hmr-1.0.16.tgz", - "integrity": "sha512-1mkuvdJD/VMTDyjQAMK6C5s0tXkQuHKT9XUMSmIrxSmrhFGpjXq5evZimakBwx89NdhMRvli8r4JrwgHRmvgAw==", + "version": "1.0.17", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-hmr-1.0.17.tgz", + "integrity": "sha512-iP+LM7V/ZAaKIJcY8IMwmu/jq9lsphaGdgO5mD6M/ekL7RSy80ggdnTYTlVJRUPvp4zngHZkBt6DZv+WCyER/w==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.0", - "@deepseek-ai/cosmokit": "^1.8.2", - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/cosmokit": "^1.8.3", + "@deepseek-ai/schemastery": "^3.18.2", "chokidar": "^4.0.3", "picomatch": "^4.0.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-timer": "^1.1.3" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-timer": "^1.1.4" } }, "node_modules/@deepseek-ai/cordis-plugin-include": { - "version": "1.0.6", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-include-1.0.6.tgz", - "integrity": "sha512-oEN09rd8oNoE/eAPrERaYFMO5HlPeFTH1QIp034R+468akhjBbtv/6DTwfp20wXggcCwmE4znS32TpQX6jJTyA==", + "version": "1.0.7", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-include-1.0.7.tgz", + "integrity": "sha512-sUvJVZ8n/cxgPrPj6UNDujUpfxja7+9kfrRTtltQ8clOB5tl2eMIWIIT7HwocRdHIXdOl8sEc8FWZIQw20yoUA==", "license": "MIT", "dependencies": { - "@deepseek-ai/cosmokit": "^1.8.2", + "@deepseek-ai/cosmokit": "^1.8.3", "js-yaml": "^4.1.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3" } }, "node_modules/@deepseek-ai/cordis-plugin-loader": { - "version": "1.0.2", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-loader-1.0.2.tgz", - "integrity": "sha512-/BTyW3Me6qVABY0ebUYsP/NcGoKSt8UIQeD1C5Tu+Iu8WHf8MAihA+cdEfuETCe9TB1Sz7RMEV/gpAUOMllKnQ==", + "version": "1.0.3", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-loader-1.0.3.tgz", + "integrity": "sha512-6q4KOh7lczfQ8tAQiTKOrVQ1oaGw//wUz1a1dnCPOPoEHfkEqEUE5kl9+7UGtUX+e8xmIVM7p+hXMyNNsV+H0g==", "license": "MIT", "dependencies": { - "@deepseek-ai/cosmokit": "^1.8.2" + "@deepseek-ai/cosmokit": "^1.8.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", + "@deepseek-ai/cordis": "^4.0.2", "node-addon-require-builtin": "^0.1.4" }, "peerDependenciesMeta": { @@ -1127,96 +1151,96 @@ } }, "node_modules/@deepseek-ai/cordis-plugin-timer": { - "version": "1.1.3", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-timer-1.1.3.tgz", - "integrity": "sha512-W1sdcvn1IGaw+9tNGP65bjYyjcQSbwmnVaYs4iaeUj0FQrN42ZeM1h53ajpTm8MXLg62KHSdB1Hcv6N28zAypw==", + "version": "1.1.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-timer-1.1.4.tgz", + "integrity": "sha512-GfoSA2XY2MdIqtTrPHYhl6Rq1bxGO6Vyo8etCRE3dhbquKhreeXtRwzdw3sgWelze56R4cn+OXjzbqqN4YHtIQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/cosmokit": "^1.8.2" + "@deepseek-ai/cosmokit": "^1.8.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/cosmokit": { - "version": "1.8.2", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cosmokit-1.8.2.tgz", - "integrity": "sha512-+WOV3CAvrpu3Ei0G65fGNMX3usRdAyl87TZW2DSZhKr8h1S1VBlQ8GbxOWDc5DtRIvl0Hj9GxvZzqhv51277MQ==", + "version": "1.8.3", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cosmokit-1.8.3.tgz", + "integrity": "sha512-UBl+x4P3v+llf98V3Ws+i3NTPFi1wb0YNRxfO67Qf/8HAE5RtWvrSbEJLJ4n0PxJjkizc0PAmRf2jDzd7UOahw==", "license": "MIT" }, "node_modules/@deepseek-ai/dsh": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-0.1.2-alpha.1.tgz", - "integrity": "sha512-0bt78CN13/Omp0leCr7qNXaqpWMMm6i+Lu8tp2A7QUX00PqPHYKBs0s6O/mAYYmzZWNvcASmc9i7P7q1bTfEig==", - "license": "MIT", - "dependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-hmr": "^1.0.16", - "@deepseek-ai/cordis-plugin-include": "^1.0.6", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/cordis-plugin-timer": "^1.1.3", - "@deepseek-ai/dsh-acp-app": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-instructions": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-tool-presentation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-app-boot": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-base": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-agent-preset": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-cordis": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-command-compact": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-command-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-compaction-basic": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-compaction-tool-result-pruner": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-cordis-client-runner": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-fs-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal-round-driver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-headless": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-hooks-claude-code": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-hooks-codex": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-jobs-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-mcp-client": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-persona": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-plan-mode": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-pwsh-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-pwsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-schedule": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sdk-app": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sdk-minimal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill-filesystem": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-terminal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-terminal-bash": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-time-context": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tmux-context": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-ask-user": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-bash": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-bash-persistent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-cordis": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-fs-search": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-pwsh": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-pwsh-persistent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-ralph": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-skill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-str-replace-editor": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-subagent-control": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-todo": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-web": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-workflow": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-web-app": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-webhook": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-webhook-github": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workflow-worker-thread": "^0.1.2-alpha.1", - "@deepseek-ai/schemastery": "^3.18.1", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-0.1.2-alpha.4.tgz", + "integrity": "sha512-jhFl3QAZYIOKPSC6RDclhJ8cpKsBpugZpemNVOYErvyPuvtU2pDTgUErppIZJra1986+nfq2PTLlsnYYRFN/jQ==", + "license": "MIT", + "dependencies": { + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-hmr": "^1.0.17", + "@deepseek-ai/cordis-plugin-include": "^1.0.7", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3", + "@deepseek-ai/cordis-plugin-timer": "^1.1.4", + "@deepseek-ai/dsh-acp-app": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-instructions": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-tool-presentation": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-app-boot": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-base": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-agent-preset": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-cordis": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-command-compact": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-command-goal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-compaction-basic": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-compaction-tool-result-pruner": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-cordis-client-runner": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-fs-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-goal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-goal-round-driver": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-headless": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-hooks-claude-code": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-hooks-codex": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-jobs-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-mcp-client": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-persona": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-plan-mode": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-pwsh-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-pwsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-schedule": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sdk-app": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sdk-minimal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-reference": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-skill": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-skill-filesystem": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-terminal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-terminal-bash": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-time-context": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tmux-context": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-ask-user": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-bash": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-bash-persistent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-cordis": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-fs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-fs-search": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-goal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-jobs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-pwsh": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-pwsh-persistent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-ralph": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-skill": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-str-replace-editor": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-subagent-control": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-todo": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-web": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-workflow": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-web-app": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-webhook": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-webhook-github": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-workflow-worker-thread": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "commander": "^15.0.0", "js-yaml": "^4.2.0", "node-addon-require-builtin": "^0.1.4" @@ -1226,25 +1250,25 @@ } }, "node_modules/@deepseek-ai/dsh-acp": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-acp-0.1.2-alpha.1.tgz", - "integrity": "sha512-4qO/1mZB2EjylgTx5UYIh/654oGDbomMWCadjkfebwk56tCl+xT5rl28VXL3wU3Sk4iUkmy3jrWW61xgng/Oug==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-acp-0.1.2-alpha.4.tgz", + "integrity": "sha512-x2AU+tFGdISK8pbPqa+27ZCQXfXisl9WOMvh5NMdoX0AetStumIUj7kcrqoFEm4dlPGJqlrYZHdTo1poiytU9Q==", "license": "MIT", "dependencies": { "@agentclientprotocol/sdk": "1.4.0", - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-mcp-client": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-mcp-client": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.4" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-token-meter": { @@ -1253,268 +1277,214 @@ } }, "node_modules/@deepseek-ai/dsh-acp-app": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-acp-app-0.1.2-alpha.1.tgz", - "integrity": "sha512-K0V7z0fK3fvY+08VGqheT2mlyWGHhGhb5YgMu/l+F+GAVTd//8fL/PMcjTV9htshWrkQgXv1kdO/8CeQKEd2tA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-acp-app-0.1.2-alpha.4.tgz", + "integrity": "sha512-Ob02PIE4tzN4xd4NyzXtrUsihU7v1fJ9XAC7ndAOcM8wBi5pSeFA/K5sWmUKa2s7bnQWhMEkxqkDDi1ax/qnoQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-acp": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.1", + "@deepseek-ai/dsh-acp": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.4", "commander": "^15.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-agent": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-0.1.2-alpha.1.tgz", - "integrity": "sha512-edcy7cBTTFHQrvRiNovADk1TUsRc8Nb+qiaQUYENL+wEjwbLRxbzoWFy2QHY3xQnlVc5n9sYmIowql/QJgvw7Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-0.1.2-alpha.4.tgz", + "integrity": "sha512-AX4PQVMrk7k/4GlpKCPtJVezHOhgdwYRdc/gPcfJMDPE4KEwxayT+4SDdLGcmV/UIG9ksXZHrc7rK47mlx/2tA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-agent-default-model": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-default-model-0.1.2-alpha.1.tgz", - "integrity": "sha512-NYPaCGs4azPMlr0o16y9lDG+L6SH5kkR8kOfQsQh5K3X9Qs+4XKRWmrTmrWy/Yvt18qLko50BChempRtXDQ4zA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-default-model-0.1.2-alpha.4.tgz", + "integrity": "sha512-opWYBQb85zmrc0ErWz2GmFpxhu9TNIAnu2Z8qbUKZtWW5QvxDYHsZJ0RbxC8Wz9WGf/ERyDMsh4TVPUn0h5d7Q==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-agent-instructions": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-instructions-0.1.2-alpha.1.tgz", - "integrity": "sha512-7KaFHZg+qoBUnyFwLO8TL5BeE+wasArsVMwWYhLY5KR43sZn0VrzMJhXfGzpa3hnleEs58YAc0mSXUKtUb3QNw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-instructions-0.1.2-alpha.4.tgz", + "integrity": "sha512-VHrNhdPU+XCLgMsKgAYNccAxtujUOYTKKilf0HSlHaQbiBLxvpZ6gR2BNLYc/twEUeE8m8Z5oq4iium55IvbSA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-fs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-agent-loop": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-loop-0.1.2-alpha.1.tgz", - "integrity": "sha512-6wiofahaXuMsH3dsXNu7Z6x5lrAz8zBdvKbZjGzagmD6sjZzqz1vu4p6ZV6V8D5G/BkddEC44uH5Zq/ASOE4iQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-loop-0.1.2-alpha.4.tgz", + "integrity": "sha512-cJmbJ66RcamEf8n3/ZiQ7XsN9hbJQHNEh/DU23IhA01iBnqzFjeNMu7lzp23aDut4h1Y2dAg7aB8KrK0dw3OJg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", + "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-agent-presets": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-presets-0.1.2-alpha.1.tgz", - "integrity": "sha512-7ZVpUkUwtQBbPXGPoyEsBFEonJUO8fJ3EXDLGAGKprtHftgYqoHw7Bnh3ZwIZzwJV8GtFw0CBwkAtLseHvCFlA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-presets-0.1.2-alpha.4.tgz", + "integrity": "sha512-xUhz6GYYls4qge6PgGqGlHGo39yTtSgj78PpSAz1uaEbg5PPtaU42z29aeYD2/K4W82xMErxgWKfrea0f31lWQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "js-yaml": "^4.1.0", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-include": "^1.0.6", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-atomic-write": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" - } - }, - "node_modules/@deepseek-ai/dsh-agent-spine-demo": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-spine-demo-0.1.2-alpha.1.tgz", - "integrity": "sha512-B/wESXaAIj2EHlytLuUb+cW61e+4sHQR8NavBwYNI0X3WL+Jf9FJOYQ+S5VSoHQZjigqh7YWucggltt+sKfA5w==", - "license": "MIT", - "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" - }, - "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-timer": "^1.1.3", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-instructions": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-loop": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal-round-driver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-jobs-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm-retry": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell-env": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill-filesystem": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-bash": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-skill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-include": "^1.0.7", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-atomic-write": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-agent-tool-presentation": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-tool-presentation-0.1.2-alpha.1.tgz", - "integrity": "sha512-DUHgMwDa0/UrtagTLgOEFbfReFNGJ59onY6dX96yOKbux0LP77uElNYJWHQ4G7JULWP7ytK9gaD6odOJJvvudA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-tool-presentation-0.1.2-alpha.4.tgz", + "integrity": "sha512-HBVuGU3SpwULGz2lqG0j/mMesYVBqwvcwYwNct+np3lo7PZXMtdc64gRE6aWImPYiZ3Jm3IooaYGUVz8x/YSbw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-anonymous-user-id": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-anonymous-user-id-0.1.2-alpha.1.tgz", - "integrity": "sha512-TcTerZC+WEXl+YtVGMLLKPp+WYRypjHpqwLYAe7dLBlnQ0IdYYJzsZevhwQNHcwiDREc7oxIje4JF+gxLTXTlA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-anonymous-user-id-0.1.2-alpha.4.tgz", + "integrity": "sha512-PKHme7RELLcuK5wgI6gun0WcFnTl7Ie/YNHmtrjqka+xM9b2Mo/c+fwcHlqfaJGw5yA/VeoOK+GGm1INl/mK4A==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-api-gateway": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-gateway-0.1.2-alpha.1.tgz", - "integrity": "sha512-E28JOwR7srVBfL58vf78SmTK9dWC85/bksTwvW026tryEQPSyDacYQAsCOowu54F0oSZq+kJjcOfuGNjiYPLYg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-gateway-0.1.2-alpha.4.tgz", + "integrity": "sha512-G93ZWeVendslf6CG0SxLECcHH82vKhc8FTUFP/VILJ9w/GaFUrTnOd25t+MFgMf2CtskGFSyJJtT0IF+haTjYQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1", - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-deque": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "ws": "^8.21.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-registry": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-api-remotes": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-remotes-0.1.2-alpha.1.tgz", - "integrity": "sha512-y8B6qorh4dqAqT52h5NkC03APOfE1InnQBU683CZqGo3EEBF9b7H2afVdQOi4OlJamll0WmB3ciAvDVJ9lyCXA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-remotes-0.1.2-alpha.4.tgz", + "integrity": "sha512-sKRi4WFGf+uUtizwVzI21Ht6+Y+0iEebvD0ix3VeisxdIvqtxvY0NedzE05XGAjhFWud2+SHW5/C55UG4zNIhw==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/dsh-deque": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-settings-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-cordis-host-runner": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-file-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-plugin-inventory": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-message-feedback": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-questions": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-api-session-controller": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-session-controller-0.1.2-alpha.1.tgz", - "integrity": "sha512-o+7IicYSkRtTUZBmzpOvRmaJrfUcBX717lD5MgtmFvpvz/EpNPQomPMi16ApZBBYlUvox7uLnjVcRj1/RP9Rww==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-session-controller-0.1.2-alpha.4.tgz", + "integrity": "sha512-rMbvOBAbW0+WS1QgzCX7+yHfvNwJY4TQAcNNf6t9vK2DOxOqlPReUXZ1pPqwlF7aOqLD/LB5SgVuiIqKBXWabg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-deque": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-default-model": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-file-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-native-command": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-registry": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-util-workspace-path": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-default-model": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-file-reference": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-native-command": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-skill": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-registry": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-time": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-workspace-path": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.4" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-jobs": { @@ -1529,65 +1499,63 @@ } }, "node_modules/@deepseek-ai/dsh-api-settings-controller": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-settings-controller-0.1.2-alpha.1.tgz", - "integrity": "sha512-DRFOQdEtDTZ1c6GaE3ja5SfmnyRMnutzvDQj6J9ILqUiAz/SUvSBIbYCUeqqtL5/TF/qoFYz4NMbOInq5daHAA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-settings-controller-0.1.2-alpha.4.tgz", + "integrity": "sha512-IkOAaf+kmEz73ORqIq68vH2KnmvfOBDNXxIkWUaMht2R7PjG4LVJOA5RulInqPzJMGXSCf2JabTeWzrpwveFgA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-native-command": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-native-command": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-api-workspace-controller": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-workspace-controller-0.1.2-alpha.1.tgz", - "integrity": "sha512-YLEG9Smc8v2X28fBQv71QCkI0xBtOkYRZQD9oeX603So03/UAS/9PLMmYFJ6SsnjsN+q3eWGKCnSylfXgWSXOg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-workspace-controller-0.1.2-alpha.4.tgz", + "integrity": "sha512-4PSLk6AhMYy2vUYuOLk1lTm+JIUdEHJVSsJXS3nSaMbCG7utaCCwBZLe47HLqvNksZN83jSvemD2jhkTPxkG8g==", "license": "MIT", "dependencies": { + "@deepseek-ai/dsh-deque": "^0.1.2-alpha.4", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-directory-picker": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-directory-picker": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-app-boot": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-app-boot-0.1.2-alpha.1.tgz", - "integrity": "sha512-7/LKJ+nVvPoHDsu7zmiZVFFOysxsKxJ1HSZf/oVHpcYCOK90BaYBU/5JK2JFM+cVFc8UgeZgNLVzodAIiJsmEg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-app-boot-0.1.2-alpha.4.tgz", + "integrity": "sha512-dAaSMpooHPJ/Ob6XE8YR6FmrEcb2O7W1QArdIFmfPQN+fjmAOFUVdEvP9SiEv0J1mRBi5zVOosTaEgwOi8QBxg==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-atomic-write": "^0.1.2-alpha.1", + "@deepseek-ai/dsh-atomic-write": "^0.1.2-alpha.4", "js-yaml": "^4.2.0", "resolve.exports": "^2.0.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-group": "^1.0.1", - "@deepseek-ai/cordis-plugin-hmr": "^1.0.16", - "@deepseek-ai/cordis-plugin-include": "^1.0.6", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-group": "^1.0.2", + "@deepseek-ai/cordis-plugin-hmr": "^1.0.17", + "@deepseek-ai/cordis-plugin-include": "^1.0.7", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4" }, "peerDependenciesMeta": { "@deepseek-ai/cordis-plugin-hmr": { @@ -1596,412 +1564,314 @@ } }, "node_modules/@deepseek-ai/dsh-atomic-write": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-atomic-write-0.1.2-alpha.1.tgz", - "integrity": "sha512-TU8ECEJSF449drQoY+rlzJb1fE2L6zQ2zii07T0Y4WLAH02mvOc32jkxZ0hZAx1XaDR/zYYUReLcI6t9cDsTbQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-atomic-write-0.1.2-alpha.4.tgz", + "integrity": "sha512-iB8KT4FN6XQWCPimPcwu98ZOyxzGsPoO3xfliuPJeMu63niwtW47FuHaMD+xb7R3HS/zM/pp9UYxFSpdJtiHEQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-attachment": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-attachment-0.1.2-alpha.1.tgz", - "integrity": "sha512-XZyzv+GULv8FNG2gjcxbl0CjUP6rBde4w/q38it8FB1gC59zysmh0Q2BOT9PZMgpuveVLPyyXXBUHkSqZD/gfA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-attachment-0.1.2-alpha.4.tgz", + "integrity": "sha512-TO623wZfm3hG/e2UEbLpxx/0bqtoztDzEhd2HW4sISB5bkaAHOWWw7u6ZeDlbYBmN6wdO24Z3pSTK0L53Zhluw==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-attachment-local": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-attachment-local-0.1.2-alpha.1.tgz", - "integrity": "sha512-G+pdboXTYcS9GxlCXzjJz5rPKr9afCjx6E0wHtI/FLtt9cMK1Qn1nbGT9uBedvXhe26GnPdGv/d0aGue62weNA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-attachment-local-0.1.2-alpha.4.tgz", + "integrity": "sha512-slB1rLE2XIYbE7G/Q7AEJZwtjBvmoebwhQJPuEO9mQQ5l5xo5RFk7FvqjucdDSLro9rwEVOr/xVS0VIQwD7BcQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "sharp": "^0.35.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-authorization": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-authorization-0.1.2-alpha.1.tgz", - "integrity": "sha512-dfxlaABGB8hcijNoiTaUmPdiJxDVpuJjZLFGCS+UuoHHOTzf0peu1yn3g+A8Wf8EgGZqtITGW+xOxV98zV1vJg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-authorization-0.1.2-alpha.4.tgz", + "integrity": "sha512-AGlXDk+OZ24qQOl0vkDY1JFlj92BCO6PGxJqfargSNEJS0VpmqjVs4yd+IAwHM9Rdc5U8iW+LXZ/5SEdEzNkNw==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-base": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-base-0.1.2-alpha.1.tgz", - "integrity": "sha512-vm1+cZXbnO3qZQ0JHB0rXHXO5eHTydz7vIXArSF5dV5rK0jF7555+uX3KUsl+7baq0/+38PoL8Qh2uxwbKu5nw==", - "license": "MIT", - "dependencies": { - "@deepseek-ai/cordis-plugin-hmr": "^1.0.16", - "@deepseek-ai/cordis-plugin-timer": "^1.1.3", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-default-model": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-instructions": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-loop": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-attachment-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-bash-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-command-compact": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-command-feedback": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-command-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-compaction-basic": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-compaction-tool-result-pruner": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-credentials-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-deepseek-llm-api-extensions": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-fs-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-fs-observation-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-fs-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal-round-driver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-jobs-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm-deepseek": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm-pi-ai": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm-retry": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-permission-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-plan-mode": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-plugin-package-inventory-deepseek": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-pwsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-repeat-tool-reminder": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-checkpoint-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-log-deepseek": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-query-sqlite": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-telemetry-otel": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-title-first-prompt-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings-file": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell-env": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill-badge": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill-filesystem": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-spill-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-spill-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-storage": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-storage-json": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent-fork-in-process": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent-spawn-in-process": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subprocess-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-bash": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-call-timeout-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-fs-search": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-pwsh": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-ralph": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-skill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-str-replace-editor": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-subagent-control": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-subagent-report": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-todo": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-web": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-workflow": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-loader": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-registry": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-questions": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-web": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-web-fetch-http": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-web-search-deepseek": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workflow-worker-thread": "^0.1.2-alpha.1" - }, - "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-base-0.1.2-alpha.4.tgz", + "integrity": "sha512-KApPPq204XO5jTy7/G2lm7YpUBy4tKNz4r573mikBugBqy+/v8uKVuC6ouQiWIuNDlvIrihI9s//wt9NT9dkdw==", + "license": "MIT", + "dependencies": { + "@deepseek-ai/cordis-plugin-hmr": "^1.0.17", + "@deepseek-ai/cordis-plugin-timer": "^1.1.4", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-default-model": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-instructions": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-loop": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-api-gateway": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-attachment-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-bash-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-command-compact": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-command-feedback": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-command-goal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-commands": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-compaction-basic": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-compaction-tool-result-pruner": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-credentials-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-deepseek-llm-api-extensions": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-fs-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-fs-observation-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-fs-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-goal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-goal-round-driver": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-jobs-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm-deepseek": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm-pi-ai": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm-retry": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-permission-presets": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-plan-mode": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-plugin-package-inventory-deepseek": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-pwsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-repeat-tool-reminder": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-checkpoint-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-log-deepseek": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-query-sqlite": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-telemetry-otel": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-title-first-prompt-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings-file": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell-env": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-skill": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-skill-badge": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-skill-filesystem": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-spill-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-spill-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-storage": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-storage-json": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent-fork-in-process": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent-spawn-in-process": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subprocess-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-bash": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-call-timeout-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-fs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-fs-search": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-goal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-jobs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-pwsh": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-ralph": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-skill": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-str-replace-editor": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-subagent-control": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-todo": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-web": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-workflow": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-loader": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-registry": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-questions": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-web": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-web-fetch-http": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-web-search-deepseek": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-workflow-worker-thread": "^0.1.2-alpha.4" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-bash-local": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-bash-local-0.1.2-alpha.1.tgz", - "integrity": "sha512-c7lDLcaXc+mC4mI+ni9oSWxFUac7tFKptle/+LI8YxRU4G15Q9mb89mjPLfVevE1WAYFNod7ekThui9q3EGG1w==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-bash-local-0.1.2-alpha.4.tgz", + "integrity": "sha512-YNkoZNLtk6sqp3GUUEOFInKlo1M4kGt638eQZxP3CU3tVpWnn+njmaaRSMmRc7cHnMtgVeeX4UyWLS9tlUxEaQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-bash-sandbox": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-bash-sandbox-0.1.2-alpha.1.tgz", - "integrity": "sha512-8Po4GUpE8cxKxQ+Q87o2MvALMZjCJweznYd3QNVG4DKpyyGGsa/LYDRE6I1x2RjFz45IV/iJl3d9EkRE1d1gRA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-bash-sandbox-0.1.2-alpha.4.tgz", + "integrity": "sha512-POp6eZzgHKP0nxEaQ0NobBSkf5KhdxglnfOoVUxWMGw+mnD2D6k+MZxSShRPE0zaT4jpJ+tfzP5dRLkUBETCEw==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-bash-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-bash-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-brand": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-brand-0.1.2-alpha.1.tgz", - "integrity": "sha512-qe1tAaJ8K2hgdqzVbhY/knbpVCj66vreF/1tW/ItaTPNnOnwo/tj4iOSvp60focpkekaZsh4wKUNjcM8yGpwDg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-brand-0.1.2-alpha.4.tgz", + "integrity": "sha512-k+BIZ234yxKRNu0I+qS83UXewFOjsEsLPj5XU0MQGndNHGMF2SyqD/k8apiXaNRgDzgLIdrxvITf2iIQVN/f1g==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-connection": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-connection-0.1.2-alpha.1.tgz", - "integrity": "sha512-7H1vj+4xbncSJsjsKRGmHhpZsd0fyi6OJZ4mFlTFIJyNw3pZkyMjUHkbCAiRdQKQK1uPpoVAx7blMXAcIPS15A==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-connection-0.1.2-alpha.4.tgz", + "integrity": "sha512-qwDmN9Teq8LyT+kW3+7+ZmM+wc6xV9kcMJhR59lPTZ4xmMufkYiPIjysS2fGEfiIx/vmo8JQB+R49+LVIZQRUA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-directory-picker": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-todo": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-hmr": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-hmr-0.1.2-alpha.1.tgz", - "integrity": "sha512-moueE7ysyTlMrgiGkUxlU1lwTd6xJFkKxGnmTWmeHH/v947fT1FCDFfau/Wt8Itucww4tv5GCrBRbRMAC6COxA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-hmr-0.1.2-alpha.4.tgz", + "integrity": "sha512-RsKkHeBgLbIRn2wyP8gMYBhXx9yzf1VOZr0up9fdpn27fe378GrmX58vbqN0wfNRgzEaT4WSU9QUaroFPF+YNQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-client-modules": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-locale": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-locale-0.1.2-alpha.1.tgz", - "integrity": "sha512-a4NBoG3gK204fUr7sKtXTFK1GfVOamoMn/n4f87dp8moZOEg2k+s8IQIPcUwDNQXOfAUsLYWaLRSwMzfctar8A==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-locale-0.1.2-alpha.4.tgz", + "integrity": "sha512-7Gd1nRQpGLvpqlmGHpk/aE8pZhruEZQ57Deid6hM5iYoSsh9tWLzxJxaZTStMWoDerlFbyvXqMYvWm6Avw/2Zg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-modules": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-modules-0.1.2-alpha.1.tgz", - "integrity": "sha512-gABHrxphhmHDglCqjBPcfz7haNXFtVX9mZB66v8az7oRpme6z8Vx79r1p6C+fMk0ymqSkBGD8Ajk6ggE9Y8/+A==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-modules-0.1.2-alpha.4.tgz", + "integrity": "sha512-k+PnQv5Phfn0t9QMSodUeLYh6YFmBYK1JiYRzGsKua4/4QbRsemo48wpJWLapbjkqMwv/T/M7lCqYHGmTRsyaA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-store": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-store-0.1.2-alpha.1.tgz", - "integrity": "sha512-6HFpsvOxhTZAwfqqROvOkh8K27nM2wfwf4Ia9wtr+bZlGL2gePXWj6AafVfqX+fJjpNYhTqDOHN3sjzRE2Deww==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-store-0.1.2-alpha.4.tgz", + "integrity": "sha512-5ZKD9MU82LIpgxkqvcJj9TwtLIc9UoSy2WCiSYNp/SkXowei1QjQlVfkBMi/49gNhqvOC3z0aipK6H8XMNgeRQ==", "license": "MIT", "dependencies": { "immer": "^10.1.1", "zustand": "~4.4.7" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-agent-preset": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-agent-preset-0.1.2-alpha.1.tgz", - "integrity": "sha512-xqTjYtDQi5a/r+K5jGhW/5WElHTIuQtemWTupKDH9DmLlFyFls/IVzbI/GXeEMZAK5eS7HGndauRYKzT+OUC/w==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-agent-preset-0.1.2-alpha.4.tgz", + "integrity": "sha512-7RL+SqMKgrPf1nDNA1VZFSeFV1iHSH+F72vyS+6VBhD5wWEowGmDiNzXVu54HzI9FIOYipb9wBSDz21Wqg//mQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-workspace": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-approval": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-approval-0.1.2-alpha.1.tgz", - "integrity": "sha512-vIJcFAcJ0xNbl7+G/G5bDQshzGwekr9ZUxWR3ni9jdqD/SCcmoouLU121+GnhIAHM5dZp2Y4Ce7kRsZfxWheqQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-approval-0.1.2-alpha.4.tgz", + "integrity": "sha512-gbQMyQEQrqdw3DyZC7bfWuySdZRQLQuY/Thrd0AOOrkHAd6VqUtDlp6PYfMWL1gzJuWI8RYOc7SHPYEU6G0ymg==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-attachment": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-attachment-0.1.2-alpha.1.tgz", - "integrity": "sha512-7O0n/palSqGRm/OpTTaFqPp6n2jiLtSf4rHDweVMNhUAhTyTtdNikDGXpnmXdX0Teg6FliZJpVHwxRxPjTf4oA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-attachment-0.1.2-alpha.4.tgz", + "integrity": "sha512-E3rmreNtj3zaS6IcK9ojb1YL5710R5HQokofYVf+d3TINxDfPfvhcOKAxdKOfxZeF8FQJSd8AbKGN2+cPMbaVw==", "license": "MIT", "dependencies": { "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-trajectory": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-brand-official": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-brand-official-0.1.2-alpha.1.tgz", - "integrity": "sha512-Vir/sAq63KyDh+cnZcrBIDoMzOiXc9FbKDJ5bkq3PttlYwwhgHtr9mdeHxaRTiFMTmdW2E8MNqzA4B5u/rfzEg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-brand-official-0.1.2-alpha.4.tgz", + "integrity": "sha512-hTCJOSAcBj86qtI3rs0NQLoMNA1uoXtqJHnboxORCSQgOQcNCLZISCsim5DhCcT1AX509bgGXG7U0mJINfCZQA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-chat": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-chat-0.1.2-alpha.1.tgz", - "integrity": "sha512-UNcXPYfniPi/VYss1nyl5MF82YzqR4oxH/SuuKyhuC8A8MTmxMCvvgxL+JSxGSvrZN0pM1iuhE9C2QCkJPUjXw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-chat-0.1.2-alpha.4.tgz", + "integrity": "sha512-ZQuT4LF+3WYQhGz0j1dbLsyM3RgS+3+MDbwc8DxzXAOO1s2H2Sa2hy6vBncqFEXUTrPazLifbDcXDiDkIQ4YYQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-approval": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-layout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-workspace": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm-retry": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-stats": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-util-workspace-path": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-commands": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-commands-0.1.2-alpha.1.tgz", - "integrity": "sha512-6t+Fx/O3HJFFWfEIp18ANevISv20sJ20axjIFtgiSeNyR0TnLLsT0SWRbB2PXkZPLXOpINTOiR82Qd5BTECGeA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-commands-0.1.2-alpha.4.tgz", + "integrity": "sha512-zrYu0Ioq9atPdKr/BLv+Tkm6wMAb8QRHf6FVNFouUeTOyvzRwq9yTw2O3qSeWcDn0wGnaBo/zGFr3aYWexJqYw==", "license": "MIT", "dependencies": { "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-conversation": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-conversation-0.1.2-alpha.1.tgz", - "integrity": "sha512-Ftg3SyIyfa6La3iNa3F+MF5zenSosveJ00Ce4ltxtZCfA1+5tPdvwfG6a2Vml3cxaJELbOrkghS2ju2VjLODWQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-conversation-0.1.2-alpha.4.tgz", + "integrity": "sha512-ZPFKAKA5g6hziAevGdGI940uoy6I7LPnybaU/FLxPG7x4QGSftfyVj0z9f1WwOUFC9CH1aN/FsTnFc+M1RLxEA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "@lexical/history": "^0.49.0", "@lexical/plain-text": "^0.49.0", "@lexical/text": "^0.49.0", @@ -2010,253 +1880,130 @@ "lexical": "^0.49.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-layout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-workspace": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm-retry": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-permission-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-plan-mode": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-todo": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-util-crypto": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-util-workspace-path": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-cordis": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-cordis-0.1.2-alpha.1.tgz", - "integrity": "sha512-BgtrauOjQeNqbwSZ5m32WsiQN9sgsofDesja8crHfj4CK5dXZdK9/gJo7Z56Nwft0rpSzS+drG0HNVo3/BMH5g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-cordis-0.1.2-alpha.4.tgz", + "integrity": "sha512-bruQSFMQrZT4UDHnX4UjXHp2/bbvsscRwzDPPC/ojh1HRismra8DCbKr/2OmeMPulOrle8vFYSFfPYwQFi6eYA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-tool": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-cordis-client-runner": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-deliverables": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-deliverables-0.1.2-alpha.1.tgz", - "integrity": "sha512-wCpocIPBugYqWSWte0A2s7DwjP8qc5eNkh/I591WeHprTcNA42THKH52XIk0afoRQ1t9HUfFdZzgOtwPX/JVBQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-deliverables-0.1.2-alpha.4.tgz", + "integrity": "sha512-8t8bw8DZKOpxAhLGqtj8TNuxbEBPUNnl2sBIfOGCPTnSjVc6am3PWv8dejHWknn1CXM8PDwjCiqP1ipuUnHEVg==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-directory-picker-browse": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-browse-0.1.2-alpha.1.tgz", - "integrity": "sha512-TqiV1TU0GDGsZQliBMVmvrxtu55GNiKdYcr9RmTEiTRVR1WCqtys+GQ5JsWEU+mghpp79R8rWARSlZdVJpBhxQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-browse-0.1.2-alpha.4.tgz", + "integrity": "sha512-KKm8HrNuKGEQfVUKGCT3DD8+49hppq4fmHiTTDqGMzrjVNL634ZwIeOGQiOnZuknePuCrluXbLDByjI5WSV43Q==", "license": "MIT", "dependencies": { "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-workspace": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-directory-picker-native": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-native-0.1.2-alpha.1.tgz", - "integrity": "sha512-bRH8JJazWGl/qrI+DYtOCLLvFj2gcfWYYwAkUP3Z0/a7PYNQPovsWtqfVMP7oj/jWf0XruZ8UdeDRCITxBdwkw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-native-0.1.2-alpha.4.tgz", + "integrity": "sha512-dUvIJ1Q7iS73xsI1jQJG+InuCCO+W9Ufqsrz6yWgX/hvH/eNgFn4q7WeNa68VhXsG3BNPVprvhSr4DUgAxnOoQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-workspace": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-goal": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-goal-0.1.2-alpha.1.tgz", - "integrity": "sha512-AbKCBnNnCp9JVmx+3W+yuz6Wrx+QoxMKO6b+ii4tQ8FZ31OkfZR8ap4pVTH/aXEwlTKPhbN2+IITMQzO2fv/KQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-goal-0.1.2-alpha.4.tgz", + "integrity": "sha512-zo1IPpUD+TpnIToQiVNn7SsigJrU4hVcV3Og+st2ipG6kVgyaxlotVV4/fbAryP1DPg7LQHOUA+3WnR/cfeSKQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-input-trigger": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-input-trigger-0.1.2-alpha.1.tgz", - "integrity": "sha512-wSigBqG2GoVLdSBJay2S+ijFuqR13ryV6IU0Wp+/ZZcrOGBG1DbNJNXU0bkaZ8t9WwCBSRzB8AwFjyZdQG/0kw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-input-trigger-0.1.2-alpha.4.tgz", + "integrity": "sha512-U/RuMUFnTlH7spNniizY5qnktKi+sX4Of1f3PP6OqYXw+g9SgNeMDecUeDY4lgNfoe5XNUl99VbYSvmU26Y8ow==", "license": "MIT", "dependencies": { "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-file-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-jobs": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-jobs-0.1.2-alpha.1.tgz", - "integrity": "sha512-1vpc9gv8XCUegCrtyESyoQYFnite+iMLLDNCwjxlDbx4MpFwo81CseTeZ6LNsXAxMmLW74fuKI0fdAhD0VT5ng==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-jobs-0.1.2-alpha.4.tgz", + "integrity": "sha512-OBnpAkIaYz8tdgjBGd/8a2ALM9+CY7w7XDu8NTYF0I25Z2sxyUlRHSP0KAwutWbmsvWHuQdXVDdUZqZ8PAPBSg==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-layout": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-layout-0.1.2-alpha.1.tgz", - "integrity": "sha512-7lMrV/3IGQx6YLB2itdzE6kQ9vL4AZsPnNq4/XhkGZ0ZR6FOoxOoyJhuH9ee0xgxzlVfcKCO5uKVZa4Q+Y7Fqw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-layout-0.1.2-alpha.4.tgz", + "integrity": "sha512-1IvavL1y6ha6xEFwyCFHtY1j24PRZR0E9tdY1Mn1yIPg7M5mjV/cbpMcwf5A/kUgSCDn/fKYBNa65AU4/Y66Ww==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-theme": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-message-feedback": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-message-feedback-0.1.2-alpha.1.tgz", - "integrity": "sha512-WbGCbhGHd4s8CqmouLmaCkIBZBMf/xigpoidRnF1CJw9RuPT9CHNAQV1qz+RdvegAybFeDGdKmv747gitdp0HQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-message-feedback-0.1.2-alpha.4.tgz", + "integrity": "sha512-Ut3WNeQ0Env7+cAfJIseVCEI9njlBj2xRwqyoJn76jRg2h52IQsZUQ+Idkk1qHzHmHWZqIna5OEV6OGzlpevlQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-message-feedback": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-model-selection": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-model-selection-0.1.2-alpha.1.tgz", - "integrity": "sha512-5qGPQ9BRsaHCLherRUQ3Fvy2askHEVcKkUfE/OVpLOO56f6MXLY6crXek/FH6DETmeatGPM03UPXT0WJ8ZbulA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-model-selection-0.1.2-alpha.4.tgz", + "integrity": "sha512-+QwSxaDzi9lCWKRbKohuwl1b/8jue0ojH8+pc7I6ZijFNhqin7jLtPAS7XThvjMoucuWrrPbYrCWldu43S/R4w==", "license": "MIT", "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-permission-presets": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-permission-presets-0.1.2-alpha.1.tgz", - "integrity": "sha512-lHFa5VZgixDUZ4CaXPi+TXWtfytNDDceUWXKfxhPYmNwNgucv9+q2pBalG4gd9gRjjmHnyn2Cp4VCNiFminyyg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-permission-presets-0.1.2-alpha.4.tgz", + "integrity": "sha512-0G0OkTHy0yXMuA/TfwpIMj40KkrROErjHskONT/9LP1rV/Hio+BMCOBJbH4JgcM8cbk/WWY+kBNus4jhgoqddQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-permission-presets": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-plan": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-plan-0.1.2-alpha.1.tgz", - "integrity": "sha512-KjfgQzxCuGn+NEDggOJyyv7wO6OWkXA0pTkXAOsGcVkITdMcsL0Ceei+Ggbu9BhC6jMNu6iUhXU5R3t+NQNpNQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-plan-0.1.2-alpha.4.tgz", + "integrity": "sha512-bJAtMvTZNn6nMt+nWaJx/DUBXNgCCy5Et3HKxl0ERUCaYWGb0eDl/0HN5Biu/mAkGFwNDCnhKv0WWzZkbmxjbQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-plan-mode": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-primitives": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-primitives-0.1.2-alpha.1.tgz", - "integrity": "sha512-IEKgznihbYzQeZnKimA1/FKTJ67HuS2asvYfKqxHAnnLVJWcuG2WvG5yQz3pJD0RfXDzGFVH3UYwtMpG3+IJ6w==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-primitives-0.1.2-alpha.4.tgz", + "integrity": "sha512-eg1OUVN9IS+Gg34AWcx5MMRkjNN9ZwsomO2TKdz9W729MqILxNbHuWhrorlgRGCghdKAlwFiy0khC53EEgfCQQ==", "license": "MIT", "dependencies": { "@shikijs/langs": "^4.3.1", @@ -2281,469 +2028,334 @@ "shiki": "^4.3.1" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-reference": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-reference-0.1.2-alpha.1.tgz", - "integrity": "sha512-LmLHIw04KhgUp2qZF9eYg8iZLc1DcMklJ6OaFITWhf0UVkT9WjKF5pk5DEEO0bBbgaAtzPIQ2Z3CdULp0ATCgQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-reference-0.1.2-alpha.4.tgz", + "integrity": "sha512-U1mbcT9MVDocl6N0732bJNFLiYMQaYC9PwisEBO/miO+/V1bovnMeA1fAlN1plHK/fijor60Z8ePK9YMn9H6rQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-file-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-util-workspace-path": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-renderer": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-renderer-0.1.2-alpha.1.tgz", - "integrity": "sha512-8iPRpdBr9YJQt1fjyqhfFKRtBcj28SbWIOINi2xdCqGFdOlrHLZ4S/KAyj6AmdZF1ILhHN8wDsrcLis2MUI27g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-renderer-0.1.2-alpha.4.tgz", + "integrity": "sha512-neEPAu5q/1jWR2s510dDaqJtf9Tky2+jfgOFmrpiS0t8XcXDCyDqJFtJKHcCzEbFfS8akCIrOFMe8OKrMQ4WMA==", "license": "MIT", "dependencies": { "use-sync-external-store": "1.2.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" + } + }, + "node_modules/@deepseek-ai/dsh-client-ui-schedule": { + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-schedule-0.1.2-alpha.4.tgz", + "integrity": "sha512-d/86fk7T29CFqshV6t0nqCGKD8fd8AUz4rLl9UIqHLgSDQaacrbVvnMaKxJjUgeIVjcW15+i+D9yWX2X1JHD/A==", + "license": "MIT", + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-session": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-session-0.1.2-alpha.1.tgz", - "integrity": "sha512-TXK4F+hmcoQCzGE0Qv0XbiVfQ3sssYYJbZU5vsW2kpX+KUn1PbHxFSx+XfhmEvFvD7otZgt8207lzSpQydRsYQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-session-0.1.2-alpha.4.tgz", + "integrity": "sha512-aMOwbaUbnKDUX7Ff3S6M4WDoNhqkAe5gfVfvfQcyIKIBqejton0zSlgomS3phdswEgba3DLwCEy0L29RsY5o8g==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-settings": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-0.1.2-alpha.1.tgz", - "integrity": "sha512-RQsb6oR8FMXQeC41JHNkrqCimC9VLOqBZUu3OMBmLa6md9/qjD5c8y5nJ3fqeLA5hK2jUrN9ISP0kbfRK3XqEw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-0.1.2-alpha.4.tgz", + "integrity": "sha512-3cNKMjhbZ+Y63nC4JHmjhvDWLZF+SrCBhwVtHy4oeuYxF4/AO1WiZmHvhI5aHUnH5uGe4aumSA5DJlfKYr0fAQ==", "license": "MIT", - "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" - }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-settings-general": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-general-0.1.2-alpha.1.tgz", - "integrity": "sha512-HTqPZjhwsjYY0fBdaQMPx9W0E8a0OBZZLi7QU38BQYzkKABm130NqwhopRVwzZ9W0XVG0ChNkJJZDIpI4kqJhg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-general-0.1.2-alpha.4.tgz", + "integrity": "sha512-lygWgIOFPhx1Xj8wj0vi1SKBOrxwIXh06zdlIGkFmteNkBF7kzevTkrIK4YO0xgb/8DvtplEiWy07R5Z37XWEw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-settings-models": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-models-0.1.2-alpha.1.tgz", - "integrity": "sha512-bVu7z+7Qz1AajzX+TezRmsA28MMoAC+WeVRXuQXQ6bnJXUmftqW7DFeQEY/xi/UizYxJd/jlQlDTmHuxLOTkYw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-models-0.1.2-alpha.4.tgz", + "integrity": "sha512-XK3fu4tODzvn/AG/lsWMDJ2UBztLJ6OSfLOL9A0WxpjlUosSFPnVwLP1IACQ7DrUqtWWH4UKQ4mU9urANhAo9A==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-settings-plugin-inventory": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugin-inventory-0.1.2-alpha.1.tgz", - "integrity": "sha512-teAk42zq4zjCOYn3S39HuAjrYUxJB/5kBR1oBZnHsxtN0fdnhEjtDeiMTtUpBKe3GwpOeIAZtq/EfdtU/6WJQw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugin-inventory-0.1.2-alpha.4.tgz", + "integrity": "sha512-hdAikyd51RY42J6bx1ieYjpT4TFHBtOYoz1KZQAPl644YyE9xMY9gk95+S9bPv7jRyJzGjv2UWB9fOvXIXgfIA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-settings-plugins": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugins-0.1.2-alpha.1.tgz", - "integrity": "sha512-PnQ+BbNs0oZTuwJbZWkMg/nfCIRLfTwGu1NrvW7Jn7MTtQ2b0z/buH7F+/+RkSIzB6y+wCW/riJnYG82IeBP+Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugins-0.1.2-alpha.4.tgz", + "integrity": "sha512-FTGoUq8TA4Llg47dcxM8u5d6MUycQQn7lu8TKEoO4dPelmQD+n9CRBI6nQKqNGIxDnxtRHcyqIfwLC3Lf0x7rQ==", "license": "MIT", "dependencies": { "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-sidebar": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-sidebar-0.1.2-alpha.1.tgz", - "integrity": "sha512-wqlBRP86dBc1FxnrKVCOqTYdjdoeDVG9wudZVrxdfnuNBW1kJZ5o5theJ0gSBj5x5vdPR5pxLM2o9hEfk3Parg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-sidebar-0.1.2-alpha.4.tgz", + "integrity": "sha512-XFqisSol0ZKo3D7i1oUlHVMUIP7LIE//TMsH8y5WLS6Z9h2WSqqK/WwnOiDyBThTTTT/CDKdcHIuJxAq9JcbKg==", "license": "MIT", "dependencies": { "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-layout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-workspace": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-skill": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-skill-0.1.2-alpha.1.tgz", - "integrity": "sha512-yEGCitISDc6RzbhpRKgoJ9LtNGqUkQrsyRZWSkx3eWr6HS0PJFzTn64wlRtCv+Bl9UkIk96QU1ro9AUpOcO/Yw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-skill-0.1.2-alpha.4.tgz", + "integrity": "sha512-1QG0Nul/5X9q7dRtoaI1yt9e+yD5YY3CrKI2Rh/flzKK22DUwLQWCMIPHzmGjRTvGXynHQdMVm154T1WufBp9Q==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-tool": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-slots": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-slots-0.1.2-alpha.1.tgz", - "integrity": "sha512-mkEM0/qpeu1d1R5pj3TsTvTcZPFRJ+pTLEyRnZ6jw1swM/obf0A0c0OWnIjSyU5rQWd14SfYDfM4gBqsh0VBRw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-slots-0.1.2-alpha.4.tgz", + "integrity": "sha512-ykDKjjT39WkdDrwAr3Q52/Jf1N9p5M0ppvyAhTNQiUZoVL1B+rHaXTUVFMzbj4NhRaUO41QA7Rt2DIz0S249Xw==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-subagent": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-subagent-0.1.2-alpha.1.tgz", - "integrity": "sha512-DP2QLN/6ZZwpy41N4eCCxZwCg2lMpBY3cQNukxU0jJB5OV9H7zI8JZVGudakVpMtIgQqxRtGa8XArmTUZlwtpg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-subagent-0.1.2-alpha.4.tgz", + "integrity": "sha512-gOG5YbcdLw9lesF6Gp7L0wI3c7R+ia+703BfLZzLpGoeUaighvrbap4MhtQ1uaqB2z6OvgXgFKrhPjW2R9pFTQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-theme": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-theme-0.1.2-alpha.1.tgz", - "integrity": "sha512-wNL+WatyqPqIJshBmOxQ8hLZl7ZY5ivIjEom4sd/sP88ESJEo7N4Uh41wB1znqw+0hM8oTtNVeYZZjMK+OFtRQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-theme-0.1.2-alpha.4.tgz", + "integrity": "sha512-EzOrT2dVa1dsbSqatzl/vxYhIAX9OG4AAWjJg6hHCJOJrPDUnicCYVO/IRvRg3c2b2nPbLrl6NtArOQtpHuYrQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-tool": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-tool-0.1.2-alpha.1.tgz", - "integrity": "sha512-EbSdtTQa4Q3BKCgMOde/AxTcVE8TdOM3XkhG3jBwB4RzWs0b9nj1ZezhMoNDmdVZ3kY1fNbTaTgsZtS1rvL4Zw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-tool-0.1.2-alpha.4.tgz", + "integrity": "sha512-V5JNxI5NBo0tChGv5uMJDPwUOw/98sm9rD8qW7Ryi5oeC3zJLog304YAwNXAQAotkZpJFdz9kMSoJGvx9iyDPQ==", "license": "MIT", "dependencies": { "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-util-workspace-path": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-trajectory": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-trajectory-0.1.2-alpha.1.tgz", - "integrity": "sha512-5g9ogBN9fClUGMFlKAj79ZEwlHsbFwAxeKuF2wR1423Iw/VWgHx3IcIgY2GKJRxAB62JHuR/NiTvCAs0LdGs8Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-trajectory-0.1.2-alpha.4.tgz", + "integrity": "sha512-pLf2zJq9k/dhMmmxkiGrzPAan//uTwSDoWbGLe6YAPOgWsdZPHZX0h+590I9F2oi0NRsSph/yQncPVtiPkmcXw==", "license": "MIT", "dependencies": { "@tanstack/react-virtual": "^3.14.9", "diff": "^9.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-user-questions": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-user-questions-0.1.2-alpha.1.tgz", - "integrity": "sha512-QOcqAv8SzonZOYHTzCFZlGi/laeEhlFMZW2V8Y74/u7b88q0zm5rcTe4FZrDfa88ga4Xlf24Mxg1KMPNmehukA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-user-questions-0.1.2-alpha.4.tgz", + "integrity": "sha512-iKzTyt9/yozH55kV7Ic1iA83qBjJqV0ugNWudMB5dmZUU4G/uZaZGbbcsk2Eyd0COUJqs+Yx4/ZCWq2ItSPvtQ==", "license": "MIT", "dependencies": { "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-questions": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-workflow-run": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-workflow-run-0.1.2-alpha.1.tgz", - "integrity": "sha512-OTsMSshvLt7mZiuwEfj27j4hbfgcZI/VAqCortbc3Cb9dfs1Uu0WHS7wA/1GaUssfxUsHkI/MrWBK4osrjBYew==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-workflow-run-0.1.2-alpha.4.tgz", + "integrity": "sha512-+FxqJZThphFSUdvxZVJav3I3CW58kKKpet6cZSFZM//UvhXFQc+W/xW78l65fjLs/Xn+iITkyRGRacBv0pESIw==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-workflow": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workflow": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-client-ui-workspace": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-workspace-0.1.2-alpha.1.tgz", - "integrity": "sha512-5w3dIXnyvNdLrXVc2RHXg8uGQqrlju0tepf7/xd7L0LjdwRQE6THy+GTLm81rCFprddpbRcWILx+Pp7sK8CRLA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-workspace-0.1.2-alpha.4.tgz", + "integrity": "sha512-0o9cZnO0qpyGRBt1FFoSXudUhoCBOELPfP8zeu3pXKKHHKJAyDCPevNS/HjsjmtkddqDW85LFSzmgjfJNAC2dw==", "license": "MIT", "dependencies": { "clsx": "^2.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-util-workspace-path": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-cmdline": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-cmdline-0.1.2-alpha.1.tgz", - "integrity": "sha512-QN8Wye9QfY5doPAuDtQrtxI5K4O8L6D419z++wAU45RLfZZ0tWLLFOiPvgzkUReAejkyEVq//rGzx7xhlSzguQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-cmdline-0.1.2-alpha.4.tgz", + "integrity": "sha512-KriX7UHqFlkMkSXJ+nk7S5eJtsibE1XlNenFhqStBQs05/hRVcP9XwMltE1OGtD46Ym97TeHAZSs6JB9+3NaCQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3" } }, "node_modules/@deepseek-ai/dsh-code-runtime": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-code-runtime-0.1.2-alpha.1.tgz", - "integrity": "sha512-VZVohX/ZVf7I5nJ7PiedZfnRjVBlVPkSIRuIglG669jlm0EwPcv/RLf9/te3K5L0jzt9JNUsoSrLf3NtJDikbw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-code-runtime-0.1.2-alpha.4.tgz", + "integrity": "sha512-Fc+wpOUc8XbZ2BfVUazyj+LLAvNeiVBqA5ZRxfp7gkmHN0nE+PWvfrDoklWCy1W3aCpBGUW5oPgEXd6tmJ+kYQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-code-runtime-worker-thread": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-code-runtime-worker-thread-0.1.2-alpha.1.tgz", - "integrity": "sha512-ONKYnJvejOU6Zxz8u78D2l+hZQPHDfd9l6stZRB9peHQ75gNiLSeBwf76jtuC1SS164D04Av3gqmmBYrY2eWAQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-code-runtime-worker-thread-0.1.2-alpha.4.tgz", + "integrity": "sha512-15ZlIlruqOn8nkNtr0Hy14L54xTfsr4KfQmThWPAKlit/YGFnpy2B286AOwa4pnW2z3H1RM8OnYATqOIeYCMow==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-code-runtime": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-code-runtime": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-command-compact": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-command-compact-0.1.2-alpha.1.tgz", - "integrity": "sha512-joBP2+jdgwQ7G33D63ZqIBAtuNo4SvkBjhbuHZMxaOiHI3otF6o3i5Il0GtMNb0EaaaeXLLcsV1lb6jC80YOzQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-command-compact-0.1.2-alpha.4.tgz", + "integrity": "sha512-/UcKjAsB/DPdUjdTx46XN8F2nwxG2KCWeGOLaTBj27El6cMsoAfzB4a09ej5bfzK4asBCFGxlOpFs4HiEnhCqQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-commands": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-command-feedback": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-command-feedback-0.1.2-alpha.1.tgz", - "integrity": "sha512-Om6Q/9W+CybQG4mOMeRXjOjC4XRmN7Af6yKuggAMXZt49w1s6l5/92P0TXmRJXLE5L4nUJLDD9SPI+yZvSbOdg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-command-feedback-0.1.2-alpha.4.tgz", + "integrity": "sha512-nuuRh81ONu0vHA6G2HSGks/Wr9ImysMKOPW1NocTmVEzQhNqN49Rs7WU/6cDTHEEEjOpb73n/FxERLUuaMq86g==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-anonymous-user-id": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-telemetry": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-anonymous-user-id": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-commands": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-telemetry": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-command-goal": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-command-goal-0.1.2-alpha.1.tgz", - "integrity": "sha512-cLjdvxnjbd3PK8uTmTd9dmAdt5mQSo0WwNBoSe9VSuCJ+zTerBm5tjrVJ9BfbjtGFoyy3Af142ZsUYhZ1UVaPw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-command-goal-0.1.2-alpha.4.tgz", + "integrity": "sha512-qa+wwjz80X1iUVmBwEiYr7dy6QF217VkWLk58FfUsHKvQn+v/TE0YEjfWnr4o/2umud6Dhfi3wK0Eyy9QhxWoQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-commands": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-goal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-commands": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-commands-0.1.2-alpha.1.tgz", - "integrity": "sha512-QslcDoAkya8q97NL4hB6imnB8gpvK/Mo8qKNvHxgGXMurCAfXPD8wOfBow+RtXG0Mf1SYdYf1INf7htp6cfdTQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-commands-0.1.2-alpha.4.tgz", + "integrity": "sha512-rt1jYOrc0WzfF93tal4mPUxJsD9fRIANv0MWeePF2pPr1nH+rr3kUiW/KhsZ1JSuRguLp21FU12ilmIYcIQvzA==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-util-crypto": "^0.1.2-alpha.1", + "@deepseek-ai/dsh-util-crypto": "^0.1.2-alpha.4", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-compaction": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-compaction-0.1.2-alpha.1.tgz", - "integrity": "sha512-1XP5JttZYbR/SaY3Z1ckqxXSqd0KwjaQXQvtCPokN8F//1dCV9ahpv2WtWk+06tG1Yq2mI01JJlytqQ/s6XPtg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-compaction-0.1.2-alpha.4.tgz", + "integrity": "sha512-MHKL+pf8Sn7cCc0OrDf8Vu8E4raRfixIB4wG1ioD8auYENOUAzNCHntZpCeDrO/XLOTr1tBnvZEdefmQP9/2mQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-commands": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-compaction-basic": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-compaction-basic-0.1.2-alpha.1.tgz", - "integrity": "sha512-rfOnmHdQx7/PKWWXkjnodYgbiYoGoFwj+GRjpsBb51S2rFPvGTKNhBgYaHRxuDCIA8DqUOL2p7a12sJ5vhubAw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-compaction-basic-0.1.2-alpha.4.tgz", + "integrity": "sha512-tld2xupZCAVEg54ut2sSY7w5/+ilWjI8R6Nq0Q56H36NHEwBpeFrNodwbQZ06gUQ1yucWQIf0KoSzELOhw8mnw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-compaction-tool-result-pruner": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-commands": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-compaction-tool-result-pruner": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.4" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-compaction-tool-result-pruner": { @@ -2752,651 +2364,642 @@ } }, "node_modules/@deepseek-ai/dsh-compaction-tool-result-pruner": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-compaction-tool-result-pruner-0.1.2-alpha.1.tgz", - "integrity": "sha512-sbzbVqJGS1gzfL/0dOUJdrfV61bGcy76PWvtd0uGtZmcXP5OwhPEk1zM4/QC9qoMAbCwjeHmiGgASOCr2cfQpQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-compaction-tool-result-pruner-0.1.2-alpha.4.tgz", + "integrity": "sha512-y2IRlXf5RXtCnn/HlKkEU7iXYL94ujvx5++t2UBiclHxIerdyBHpa0fLTQIdz0p1QDWvjDZ7agGUf70WT5UwZQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-token-meter": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-cordis-client-runner": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-cordis-client-runner-0.1.2-alpha.1.tgz", - "integrity": "sha512-89I3iktqGSQCP3xH+OGZcmSlprqZxnVNhUoFQxgQCW3L3DVpQZqmQjbtcbz/SA/I2BCBEBczdam/x9Fmnpej+Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-cordis-client-runner-0.1.2-alpha.4.tgz", + "integrity": "sha512-KhUX5lRHojO30oTVSaywl9ZyBXSArYDW96EBX5RFVPaiMkqEiKo2H3torSyivA3UuH5WmDHEycK6mztK2qcQEQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-modules": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-theme": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-cordis-host-runner": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-cordis-host-runner-0.1.2-alpha.1.tgz", - "integrity": "sha512-B/cI02i2nh3XdZmPWit3qNfH/EW4tzHhEOhjFyTQMc7c9Ff1aS+DX3VqtITEaZasD6xS2zc7T8RXwB0NLOT3SA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-cordis-host-runner-0.1.2-alpha.4.tgz", + "integrity": "sha512-rBKV88rrK5+Au1wzSAuPZACJcgt9tBQHujupFt3aqS1PAM47iyg6ES6SN9Z62yeAvCZAamMRHv5liOJwB8UTQw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-credentials": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-credentials-0.1.2-alpha.1.tgz", - "integrity": "sha512-SDrh0MwzxY2OZWmOa5gqwJTLIt2VrxsMutnH9jqL3lP3sS0QhBXHWSLVo2XRgc0p3kb7ccP9pVzkPH51jp3wRw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-credentials-0.1.2-alpha.4.tgz", + "integrity": "sha512-/zEW+2SQExEUC9Rjkb3UqZK6bU/VNLhfs/PtC2XYCxHrghBMX3Q/FBJKlGwMSDLvMFONgpILtu4Buf1Z6cUd1Q==", "license": "MIT", + "dependencies": { + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4" + }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-credentials-local": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-credentials-local-0.1.2-alpha.1.tgz", - "integrity": "sha512-+DxMoqrfWZDuYmVm3ClIi5NMsPXqdjedjKtVomCPxiSHvLAKW99atw5SVLqkvsLhYgCKSoroCjkNUHY4xjaN8A==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-credentials-local-0.1.2-alpha.4.tgz", + "integrity": "sha512-PLTucrqgV2iAlYa8BcDa6Wt0vR6AXtoLE1HYk7eq/esD7OqSmSlZ3SX/zkCMXQnIgSwZnlZamxmmNMZM38pD5A==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "chokidar": "^4.0.3", "yaml": "^2.9.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-atomic-write": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-atomic-write": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-deepseek-llm-api-extensions": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-deepseek-llm-api-extensions-0.1.2-alpha.1.tgz", - "integrity": "sha512-kq+G1F1RuDK6SxFED8Q1u8TJeA4mhg+Gtpu9f0umQMmSmyOr4QfbgltWk8rmlMAO29Vv0dAlu6Rj89ymg0wq+Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-deepseek-llm-api-extensions-0.1.2-alpha.4.tgz", + "integrity": "sha512-gStS8YWhTnS+ECDQJQ6nusdh4h1nBCxN4xb06W1MFdWNwHsFlbtS3H72iqFlcljaYpy4q6pTkv+awBwDgtB8qQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" + } + }, + "node_modules/@deepseek-ai/dsh-deque": { + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-deque-0.1.2-alpha.4.tgz", + "integrity": "sha512-XNWV232Xe0uv6dRYXFvzgYyBL3GjxxyuDmHy505rEzdEPN+KtKZJ4/UA8/eqesCazrZ7iQzlqA94uzTMoruexg==", + "license": "MIT", + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-file-reference": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-file-reference-0.1.2-alpha.1.tgz", - "integrity": "sha512-kakzVv9btdUfaOy3ZVTcLSxZDIbBspNXsiJFaG3fI75ngE+yoHUbmIIQ2PAu/pm6j+Nj2uJ3c2TgLpIb/Vs5EQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-file-reference-0.1.2-alpha.4.tgz", + "integrity": "sha512-jyO5D7ZPH8xaIb6Rlk7qKyrE3dQLJ0Y82XUQgU1AsGOIGYMYGDLsPi7DezLUii9xKLqoLMlghnCim5yZHJc5YA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-file-reference-local": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-file-reference-local-0.1.2-alpha.1.tgz", - "integrity": "sha512-/kvp9VZB7/NW97zyCTfLifuXLMEbjXHFkTvIJYGtY+vNgcdwX5kQ5nNjscsWS06Os2RcMOXW6mnd5AdNhAwHPw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-file-reference-local-0.1.2-alpha.4.tgz", + "integrity": "sha512-PJSwy9fgqJi5L0zsgCh/qrccK4WYGbqf0cTclS3aR9sJzEDgr9L6NhQILWtFsAgdphiOlISkHncRvP5w9e2dFA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-file-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-file-reference": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-fs": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-0.1.2-alpha.1.tgz", - "integrity": "sha512-knF417EqqOAI4/L6mneKzXrgQjEJsRXsYQL1e0EzOz8woTiFpMw0Bc252DiluZoe1jcMI6VkaFDN7igLAhA8rw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-0.1.2-alpha.4.tgz", + "integrity": "sha512-rARqwjK+5sbnpE0QSX1GJx8PnJHAySzi6vKWANydOBiPbCkaT2UgOTudPSCev4WPVf+f4yp+D4ZLAuUJA0pnNw==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-fs-local": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-local-0.1.2-alpha.1.tgz", - "integrity": "sha512-VVYqY/qenU+LHztj504ayFXxNd+nxxjPrEKsfErTgxPBAFW0cRcAFL930FNIOdaIYWN0bIqVAgPJZ+OTVqCMlA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-local-0.1.2-alpha.4.tgz", + "integrity": "sha512-0KKuDNQz94nXox13WxgiK+Iu1oGFwAhsJDqKmPXAHyBTqCWw2rKTbyoguzRMWtfBP5aGpVafWUU4gnEBCqW7Hw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "koffi": "^3.1.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-fs": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-fs-observation-policy": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-observation-policy-0.1.2-alpha.1.tgz", - "integrity": "sha512-4DTd/EKUa4eyfamgSg3k7Wm4BAaKgmEc85IxXhOTV77VmH9RNmqcmDKmCL4P/olk3oD0Rz+krJq//sDwLuo36g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-observation-policy-0.1.2-alpha.4.tgz", + "integrity": "sha512-cQjf9Txo9FnOKH1EhQx2+QCh1zZoOvl7OTgYRBTkqFK4ohuaIobyKSCp/JI0Z7eG1vJqpcAS7ICyaQX+S+3d5Q==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-fs": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-fs-sandbox": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-sandbox-0.1.2-alpha.1.tgz", - "integrity": "sha512-TLTEltMPZxld2735n/qkFGD0w6Z/7lCBq9QF72T4bDOiZFt5qktspYZQg84z4/0mAc5KdSFA2sDZ0xROdT5UzQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-sandbox-0.1.2-alpha.4.tgz", + "integrity": "sha512-PMnATdlR8ISPuAp0T7f+7Kkl/RuBiVlEACrB1LOkKQXIdetlxYZ2t1ah8Pjbt5JkUZi/J/nbspNNWHsjUpw6Gg==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-fs-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-fs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-fs-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-goal": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-goal-0.1.2-alpha.1.tgz", - "integrity": "sha512-wc21nCFlDOEBV9mbiJDXncfk0MXDg4IsBvWH6WLIHzR6s73L1Q09t4zaXOOAGqQOWePqcBa0cJRtuazUMeoIoQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-goal-0.1.2-alpha.4.tgz", + "integrity": "sha512-6UITbDkEz1F47VDim4P6UGzIgP3sEeNVPlqpqc6B4kJXUBrnxzbQyfHP+T+LQMrLq0MgkDKIi7+K1b8ZVG2/2w==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-goal-round-driver": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-goal-round-driver-0.1.2-alpha.1.tgz", - "integrity": "sha512-E0Uui/MK0cL0MX9+V0eOzKqea0ew9lbtdGCb1P+XxtZPkaS7uhPIzd2Z6W6yt/vcyUwVvnH2YD8KDteuhCwvwg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-goal-round-driver-0.1.2-alpha.4.tgz", + "integrity": "sha512-5/y7mWczCXJHo7UOnO2aHcL3e5wiApZxeE1mHHnHMl0YUsEOQTggrpWmLU2hyMBuIOROda7aaptSVpSyKHEjFQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-goal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-headless": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-headless-0.1.2-alpha.1.tgz", - "integrity": "sha512-byfgmPrJPO9aupaASuWH965LYhOqpR78kCL2BWzhVQPPpHxAt2A7GgrPhSa2FFZAwg7RjhmG3Up1QvYjVbNAsQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-headless-0.1.2-alpha.4.tgz", + "integrity": "sha512-CIXn0ZnzYU9C5E10WJRrmbXzO6ZLxU1paCovpDFdLQ7EkUKMg72zS66mj9tB+SKnmg9Hspc9QeGTZbZxhGx1Ig==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.2-alpha.1", - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "commander": "^15.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-default-model": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-default-model": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-home-paths": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-home-paths-0.1.2-alpha.1.tgz", - "integrity": "sha512-ub3dHQxjraJlZDpUew+7rEV9qBvK0AJpRgraYL2xJR+xs58RTNFIJ7ENrpcJM/57TsD5DNAWBpcQvs1t78EreQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-home-paths-0.1.2-alpha.4.tgz", + "integrity": "sha512-IfypLeLbaEHQVjoHXj45LTtv1IZCFGFDqim3mBzOmQN+Y7qz6hw3hbXN0b19CAOgFr0GhtOuHzTzZhG5fst9BQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-hook-protocol": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-hook-protocol-0.1.2-alpha.1.tgz", - "integrity": "sha512-c65mtt5NuV9Saw8UT4PrFndlp0D7uL/XnrFLklbkoM3wp5f6aaH0JPIVWTHeXseUEI01MppxV/fWpUcxJqhA3Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-hook-protocol-0.1.2-alpha.4.tgz", + "integrity": "sha512-AxezVJ3UIl06XczXZ+rL4ffcu9z/65HiDPZXtIdX6AxKQFiGJ3JfYSsxt10mEW/ycGlXdmXfS+8Coh2buAw7mA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-hooks-claude-code": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-hooks-claude-code-0.1.2-alpha.1.tgz", - "integrity": "sha512-fm+/Cdl13vGLJ1paLQ1maI7QFa3WBfY9D8ZoDtj55gAaAJ3KzL6M/7bgbIxy3Bcpf+Yqnhcte7kzxiNQMBsGDA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-hooks-claude-code-0.1.2-alpha.4.tgz", + "integrity": "sha512-H+O8euzC1vwrOoyur9zy/Ubfa9hdhqw0uJrsJcGvtgB/JnRiRKyVxiTIxUun0UzSnQgChrE63FOmcdiZGGOb2A==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-hook-protocol": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-hook-protocol": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-hooks-codex": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-hooks-codex-0.1.2-alpha.1.tgz", - "integrity": "sha512-6aSHL/L83T4zBvvTKSGhrhp8x3qLk4rSNlseHNctCO9S+DAiUUZRUv8TtPJK0AeyODM4QocigRR1Lk7Vr7YBrA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-hooks-codex-0.1.2-alpha.4.tgz", + "integrity": "sha512-dfRZLDvqC/ncXbeH+mOmHDSM5AOJn5gOylwH4KqmSMLIFDneXJPXT87seLGHa7f9U/Lrp7jT2RtUQuznHIrFrw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-hook-protocol": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-hook-protocol": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-host-directory-picker": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-0.1.2-alpha.1.tgz", - "integrity": "sha512-kW1dtiWwdCzpVqivsJHRMu0SJsDt3FcDACSgXIb8h0eHGGgvjnX8oeb8XwmFlF2+768KkRORz21ENmolszmmJg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-0.1.2-alpha.4.tgz", + "integrity": "sha512-EG3jiyuHHrm6HcEqT/y9617ZAZxpIlrMugmmhAxvZpFlvfKKgShAFd2y1Dyvk/Rj5ZaVpT0yIfNWAc3amL5kGg==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-host-directory-picker-auto": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-auto-0.1.2-alpha.1.tgz", - "integrity": "sha512-1J2LHe2FeCUzFJ0wFj0RAxS1GTL33+X+aiS2VfQSTEOqeKr7CGVL9Qbtoy11/8l+nDBG8AU3J40LftDRtemtbg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-auto-0.1.2-alpha.4.tgz", + "integrity": "sha512-b/Q3Bb2xeLl6f+rFNOchskz58hNU2SuZB5odHIBWNCiGINlKdLt0RQ+e7eORY80DHS4seNdBbpGLLV+xZT4rBA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-client-ui-directory-picker-browse": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-directory-picker-native": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-directory-picker-browse": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-directory-picker-native": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3", + "@deepseek-ai/dsh-client-ui-directory-picker-browse": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-directory-picker-native": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-directory-picker-browse": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-directory-picker-native": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-host-directory-picker-browse": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-browse-0.1.2-alpha.1.tgz", - "integrity": "sha512-YPp3NkgivbGzW05ETh8wL9wnm3rmHiw4/WE062s/aNXnmIGgq0hllwXzL8e0IrgvpR9V3wqFBjCICMXg6DEr2A==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-browse-0.1.2-alpha.4.tgz", + "integrity": "sha512-fLjiwsVMhyq+C9S8CSK13dbLf4+DoxemtInyjPbiv1Jcb2EAuwGHmrK8WdysJgtFUXQ0YD07W3BYT58yYsOVIw==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-host-directory-picker": "^0.1.2-alpha.1", - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-host-directory-picker": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-host-directory-picker-native": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-native-0.1.2-alpha.1.tgz", - "integrity": "sha512-4IFYpdho4RxcnO8zHVEfNgLBEbPWKFn7KRuhsq1QAFUYCjvP4HtQsdSh5LHiArAV1hKWBUFVoElMfAD1DsdGpQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-native-0.1.2-alpha.4.tgz", + "integrity": "sha512-77SR/cMDjkoGCRQ2lBBTtOq6GY6xaTyjn/BMtrdwa0FuYvJ9mtLeQwFECSGS5O+juMXGGzjGjnCnbmHu7uQcUQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-host-directory-picker": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-native-command": "^0.1.2-alpha.1", + "@deepseek-ai/dsh-host-directory-picker": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-native-command": "^0.1.2-alpha.4", "koffi": "^3.1.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-host-frontend-static": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-frontend-static-0.1.2-alpha.1.tgz", - "integrity": "sha512-7Y8eRM3g9L8MX3tR0ZoX/9HcYQa97KaaMPU6h4MfRk1SxqhtBr+M/reGA2uvza5XR1UcZxewwXFJgB/rNMVvww==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-frontend-static-0.1.2-alpha.4.tgz", + "integrity": "sha512-Uc+BBL+mvHmpY1TcCqlF9xoMDnb10vOC3eIMIY/AVGt1HUywItgoyu8LEys+o+JJN6fZvOeMF1VpZXoMjErY/w==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-host-plugin-inventory": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-plugin-inventory-0.1.2-alpha.1.tgz", - "integrity": "sha512-TwnX8y9fcKxNrjpzHYlPvL++ujDwJgUrG0cSdEE8WyzUenRbTpfx5dn+r4suVBFaA6q/ITbOaZ2NuJ2vVCUTqw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-plugin-inventory-0.1.2-alpha.4.tgz", + "integrity": "sha512-O5zJaHTFXIEq4UKChKQgkfTLS9YUUT0o7VWJXT+39ORq1Gse/a3KEZf8v9Y/2WhPMarAVrDXCkBZO0kQA+yNWg==", "license": "MIT", "dependencies": { "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3", + "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4" + }, + "peerDependenciesMeta": { + "@deepseek-ai/dsh-agent-presets": { + "optional": true + } } }, "node_modules/@deepseek-ai/dsh-host-webserver": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-webserver-0.1.2-alpha.1.tgz", - "integrity": "sha512-RNJu8zCAU8kIaHWphow3HTxrsBM6WPMAZ510pJuWqlxtTx472GuMa949d1uI+fwwXp/TObhVhNcwLxdrKxlOWw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-webserver-0.1.2-alpha.4.tgz", + "integrity": "sha512-y/liwQf1lEv4vYl68mJFZf7zIAffJfcc6RwxUGSlh6ViERnXWXKEUycFvBXvHdNyUqApLjYV2eqYk8VaUP9aiw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "compression": "^1.8.1", "negotiator": "^1.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-invariants": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-invariants-0.1.2-alpha.1.tgz", - "integrity": "sha512-ywoI9ebKJuIzm8gMNbPex3KYE5DcYK++7LGouiP+4kuywL0Tg/g9u6s72FCTLDoeNIDqRicnee5LnkPdRTnyJA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-invariants-0.1.2-alpha.4.tgz", + "integrity": "sha512-GL1m+FJd5UY8k4kM3OimjuWtL5L9yOtYoXbl0Zd+RM2Um4yM16jpjDaCJxkp9qtK0Lgnt6S6l9Woc2PxNoypyA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-jobs": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-jobs-0.1.2-alpha.1.tgz", - "integrity": "sha512-zh3nfZKlNA4Y4Wz762i5EcOeqarM6cDxjQrxos7emAUdFNDkrGmKroz5dDcWFGBJ1HjM3aEgbRF9yxXJOhN+/g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-jobs-0.1.2-alpha.4.tgz", + "integrity": "sha512-ayK3x4OHkn/cGTOyCmRnVGbD6OJ4m5Xhy9Vx80PNtsWHknbHNmAXFVUbkTXOWr3Kqe91L8As5NKhfkWJOeGNiA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-jobs-local": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-jobs-local-0.1.2-alpha.1.tgz", - "integrity": "sha512-VRf0yWG2ZWoc5KIHI9GLhxBUDi7RraC+FDwR7paIbX/uhEzTTmzvjpzcJngzHvHHib9E+GIdMEuuT7tIg2MZsg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-jobs-local-0.1.2-alpha.4.tgz", + "integrity": "sha512-P+Pan3EXiBWmETAhkLuztbZV4aVn6g0uBY6qiYNann+GjpESmwj5y0pCBixEVCrNal2EX0VxLyGy8PaQS5VZ7g==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-launch-environment": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-launch-environment-0.1.2-alpha.1.tgz", - "integrity": "sha512-9lhNj1BhhwTDj8LW5PoRledLqiYbhQU8E+yVcSzWD2DPYW2WVcmFV+EEIwfPe0RyJtZribLmCQuugtiO4nwZ7A==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-launch-environment-0.1.2-alpha.4.tgz", + "integrity": "sha512-UvTzRNpog3SwQSO7cgwmH8NtHNkO2HqrkL1irSNow/2rnT6S/Wt9G9nqwTi54CLfOAbL1Mw91oFsD90yMAAWwg==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-llm": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-0.1.2-alpha.1.tgz", - "integrity": "sha512-SlNM7wiFRIv7VHxv5aTvDG+562bieu3M14RsZio5enYJ5/UeS4KinNdJm9rxK+VCVUrZjJC2YsBgWOWtwYu59g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-0.1.2-alpha.4.tgz", + "integrity": "sha512-glPiqJvE2hgq77YXR7GHRykihYmLBj2aDIlodGYrMFTVRq+1WOz06+Yx+Q+zlb1SD/hjHsGgR3HTaM/bzUmRmw==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-util-crypto": "^0.1.2-alpha.1", - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-crypto": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-llm-deepseek": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-deepseek-0.1.2-alpha.1.tgz", - "integrity": "sha512-kO4B/TQvnfsAUa62gijKov9O2/oA37lbtOyZz/zRqw9I5vzyoT239C2Qtqc5gFz4xr7Cm8hsYTIRmCBuTtO+gQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-deepseek-0.1.2-alpha.4.tgz", + "integrity": "sha512-ZdifO7LwSjLKOANLU0ME4NhhYNnytoaje045lxy9kMo3pxjSxEGyB8VH8cxVfYEtygUgfnheNBRnMRBzG7UHzQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "eventsource-parser": "^3.1.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-anonymous-user-id": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-atomic-write": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-deepseek-llm-api-extensions": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-anonymous-user-id": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-atomic-write": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-deepseek-llm-api-extensions": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-fs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-llm-pi-ai": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-pi-ai-0.1.2-alpha.1.tgz", - "integrity": "sha512-hGBsCRw/uRAsqJ4409BpU10iay7Y6/m5fzahWMWeM5gszaX6+pX8pVb2t/sTfYSZeYX+eZCHRoGh31uIGZoerA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-pi-ai-0.1.2-alpha.4.tgz", + "integrity": "sha512-13+hk2JVLwyaRmyjTbrFppTJuKc/EhGIVNJwVaeVXOidRH33Yk6K/QwpxoHcNgZur9r0ihxGGThoJ5va2Vz6aw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "@earendil-works/pi-ai": "^0.84.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-authorization": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-authorization": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-fs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-llm-retry": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-retry-0.1.2-alpha.1.tgz", - "integrity": "sha512-CYUuEjlQHL+X0E+SmWGDBdXdN/nfLnLcLgBM6q2ifTm/KHMN1bMh+VUZMUrpZeSwzJSBBUwpNkiPWQzqBZ3iYA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-retry-0.1.2-alpha.4.tgz", + "integrity": "sha512-IBn2bwlMsc5e6cPR246oOU5mt1LUHnCozvpOFY8o6EB+tsV5Od/6WRkjUUr66KVv9U0G0mrDiViUldD5367Rsw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2", + "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-mcp-client": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-mcp-client-0.1.2-alpha.1.tgz", - "integrity": "sha512-KyXVX6VHaoCMya5GXDfA8lZpaxr1PIz6GozxybHclItUWY3KHLGrBU7LYFUTJJD2FXEyLHz3KOhBrwLqcO05zg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-mcp-client-0.1.2-alpha.4.tgz", + "integrity": "sha512-lwcHv2KplegG/Yvat0baRdqgiWL2vflaJHhUuKggIV1jtmrelPobc+ULVdtx7zfd3MQBdSoFBNcGNTR/zvsGWg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "@modelcontextprotocol/sdk": "^1.12.0", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-message-feedback": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-message-feedback-0.1.2-alpha.1.tgz", - "integrity": "sha512-eKvM9gNhmlwGsvOtDKUz6xPGmGJtcdT/3aJWz9mWVg4kV+cf1dvSFNWz1lbrdjVgFjOkA1MIwKOm+juyex3fTA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-message-feedback-0.1.2-alpha.4.tgz", + "integrity": "sha512-JvUSm1jMcEXy1U4JWkBwRipv46Z+cYofNJJANW6Dlp2JH/aBeqa4Vrb6H9JqBDIugQx80mDq998LK5sNhhAGsA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-native-command": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-native-command-0.1.2-alpha.1.tgz", - "integrity": "sha512-jfU6A5T+1O/UMPoCwkFERZlEDvrWPr6jeoSEB1IEuixcpnOQPgsoNmHVnLPKfxe80IzqhlkTbm4E8pc6RxI/ew==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-native-command-0.1.2-alpha.4.tgz", + "integrity": "sha512-3vjKP8Ui/Kg7h1bstPjHBn13APMvIX9DcbP3aBeTbqdzpEvLh71KBOnNVfv92AF0+w13Ggq0PZIgdy4LEpOdXg==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-output-retention": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-output-retention-0.1.2-alpha.1.tgz", - "integrity": "sha512-V8hnqGos7IYCzlAjdBlaBi9I6e+eB26RGfQhJALVO2+jXAhrNLyNqQUmMu72MK/RHVTCpzzZWq3MpZySdCDC+Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-output-retention-0.1.2-alpha.4.tgz", + "integrity": "sha512-mcK/RAG1dAOoqSNN1VPaWWtm4FdhL1QEi8HqezLt9eFCC6VIbd9el+hT5gpJpfIdnO2Q/xbTfamZHXPxuZymHg==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-permission-presets": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-permission-presets-0.1.2-alpha.1.tgz", - "integrity": "sha512-svsUjRagORDdSWcR9A+uct2vbmwQ3BOCArzdqozF9uU6WLZtn3Dapam+SnnoHHJ1ybzKuDgLCxSggV8tDVAk4Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-permission-presets-0.1.2-alpha.4.tgz", + "integrity": "sha512-/jJv2DAV3TUvQFE/zO2anXTAjiNpc9VjNQPAAgeT/WneAnYzKxRXZ7gIn4gd2SV/gHAxTJOI9vVzt259odnESg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-commands": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-persona": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-persona-0.1.2-alpha.1.tgz", - "integrity": "sha512-zL3uXcKthMiwIcwssmP7iz3nWxoYYBY4J4CLXM2TZCxwxG3y0LwcgYOFkVVZW78jvyIrhvxd2NnKTDc0zcVOwA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-persona-0.1.2-alpha.4.tgz", + "integrity": "sha512-fZtcIZnmPQpfdcHqjPeRxcx4FXCuy6HPgiEEPBUgdARZ7PBxhAtob1naD4TYXz8zqa1bfWJf0wBk+5g8HRdC8Q==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-plan-mode": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-plan-mode-0.1.2-alpha.1.tgz", - "integrity": "sha512-h/veBFbCZsxedRHYVLAvTcdn9FfuMhinhZ5ZoeEHWW0xhtphYsID5TzhzGdFLyxinHzT74Jhb+fhzBVwP2jkeQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-plan-mode-0.1.2-alpha.4.tgz", + "integrity": "sha512-nJ4RHUTlOqIAFzXae4V6RevYv6xDRvM+uY144rNXJy4yjYP7Wd75HZBlMd8XMh/jQ7oubVZkkzpzl3kzN0EluA==", "license": "MIT", "dependencies": { "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-questions": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-commands": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-questions": "^0.1.2-alpha.4" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-commands": { @@ -3405,21 +3008,21 @@ } }, "node_modules/@deepseek-ai/dsh-plugin-package-inventory-deepseek": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-plugin-package-inventory-deepseek-0.1.2-alpha.1.tgz", - "integrity": "sha512-fWVT/18dyvwzrbLwwyFs0FASpYn1UznxXeh6OuycQCYNtuXS+jWX0FgyMprJFY4DxD8lNDGZvA8l8LDx2UUYug==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-plugin-package-inventory-deepseek-0.1.2-alpha.4.tgz", + "integrity": "sha512-D3kBiJHpX241JzHy3j4zI7tlC0LKzYljLkkpvab9WiKZqf0A+OAyjRtiDJY175/wWx83LnJdqTwpbylqAPK1wA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-deepseek-llm-api-extensions": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-deepseek-llm-api-extensions": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-agent-presets": { @@ -3428,362 +3031,365 @@ } }, "node_modules/@deepseek-ai/dsh-pwsh-local": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-pwsh-local-0.1.2-alpha.1.tgz", - "integrity": "sha512-UsxuuotTpdwDQfErGcPl+wjIVE9jlcfOSfTX3AG4eulN6sjuUM4rAl+Aga5jYeVgQ95G0QRsr4gF25XFW8PnHg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-pwsh-local-0.1.2-alpha.4.tgz", + "integrity": "sha512-GEmcY4++7LANJLk8d0UGLFRcdjpHerzf5+kXqFKCR48vtgDLZnDvejMWk5g0AP07F+YPl2Yy0+RSQhaH13Mwow==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-pwsh-sandbox": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-pwsh-sandbox-0.1.2-alpha.1.tgz", - "integrity": "sha512-ft+TOuAYmFLcgnoZy4epJAXsleWxFI/Z7pA+5UVrmZYpETqZ31mA+6EIA/68GDasr6KsZ5eFKL+8HrdvFvqEzg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-pwsh-sandbox-0.1.2-alpha.4.tgz", + "integrity": "sha512-7Gq9y6SzRis9NAAp+GlzU0xOgTqyGHZm6SUnP2GK2lc57GUnXabck12T4nw/hrsA9Af6lPn4R895HLoWRb6JXQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-pwsh-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-pwsh-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-repeat-tool-reminder": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-repeat-tool-reminder-0.1.2-alpha.1.tgz", - "integrity": "sha512-P/cNMa3v7TCbT8pkP6jSSnbWh7vbxOE9a9Jr4U3ZaWavzX9t7aIcI9dATVMcAl+EYJmuY3j00X63uF5/j1/PTQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-repeat-tool-reminder-0.1.2-alpha.4.tgz", + "integrity": "sha512-HhtSVnsiCEOLmk/vmn1K4awEsSdDVDGbBzVpyrMP63x0qA9fPlPrDPIs9qqfkZw3iWd9XIxVVwl1Vyp4s+dDHQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-sandbox": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-0.1.2-alpha.1.tgz", - "integrity": "sha512-BYs7ckRk9QWcwJLcWKPXM9RYPcCuZshyn53dIXxtyls7xLE0w/L10gBEaggh0gZah6TpwpxDOokLP0vuLIsANA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-0.1.2-alpha.4.tgz", + "integrity": "sha512-Yv76qW61w6m3IXD1jjvMCiLNL3h5k6QZZB91fX0PpC31m+/OEDqvbUhMse7ZzOHLy/1uxd7Hv3SBERmLbigWpA==", "license": "MIT", + "dependencies": { + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4" + }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-sandbox-local": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-local-0.1.2-alpha.1.tgz", - "integrity": "sha512-maLq7JhCqftYkSrPFPhhQd3JsMFt6S26ZAQi1VyBjQTD/TcPv3IBDVwnYHjadIrfgO0AqtJnTgC5TPeeGCSPjQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-local-0.1.2-alpha.4.tgz", + "integrity": "sha512-XeFZKkB8N4oyvgHHBQdZcChtxS5nRz6lhpznKwqxdAzifLrc4nQApYk2Q/qecBFEyS0RFVqVS3JRRqCbXcQjOQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-sandbox-windows-acl": "^0.1.2-alpha.1", + "@deepseek-ai/dsh-sandbox-windows-acl": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", "@deepseek-ai/node-addon-landlock-run": "^0.1.1", - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-sandbox-policy": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-policy-0.1.2-alpha.1.tgz", - "integrity": "sha512-GexpIZkbY6RYRAFbwVgoJNwi4u5eRWNQQE8Ot0L1qWEFfp1MobAFBQDG55Sl6v80IPSGiGPOY1K2lpS7+lYt2w==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-policy-0.1.2-alpha.4.tgz", + "integrity": "sha512-WJLOR1xkj7n0XQ2tbaLnwVq2/QWBG/o3J9MmcHCBosBdJiq0rG9YiUyTCwz9J8fqXFykC7Aza5p3G81SbTDE2A==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2", + "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-sandbox-windows-acl": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-windows-acl-0.1.2-alpha.1.tgz", - "integrity": "sha512-oRBY9ZUe91/jkjXgzXTpL39VkrR+VhCfMggZmSowVQZtbj0ITSsiZkH/93QbH5dUs+aTgKx0e2SmYT39iqmCug==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-windows-acl-0.1.2-alpha.4.tgz", + "integrity": "sha512-v+d07cPTRD6/O+M2+JPOv6P12ABWBlL8v4IxkGW+0Fity8QayUrZfLZE7zzcToVMjtGkQ/eAwWA4FHeMPAX03w==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-win32-process": "^0.1.2-alpha.1", + "@deepseek-ai/dsh-win32-process": "^0.1.2-alpha.4", "koffi": "^3.1.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-schedule": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-schedule-0.1.2-alpha.1.tgz", - "integrity": "sha512-fzMG9xYV5sbyKXgQ7Cijf9TVpqM32Jx7M+huBJPOYoMg2deweg3qEFA+JwUyaNwrIQ+YnZewTf7mWHNT/lAMZw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-schedule-0.1.2-alpha.4.tgz", + "integrity": "sha512-7caHh/KMALBgVXtBIe4udWCP9hOaeZsg854pkh9RIs3uTsj56/F5IPf6jrjOB9fN0ACz9pdeK5TSbMpoca9e/w==", "license": "MIT", + "dependencies": { + "zod": "^4.4.3" + }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-scope": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-scope-0.1.2-alpha.1.tgz", - "integrity": "sha512-apSh8Jdocr+65Ka1fuOfRUIm8G/ZL6mZ8WH2KhfTTsQDr1TLDmaFGQMRtO4zukwfhT4uIzo4e3y/bJfvPnPj1Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-scope-0.1.2-alpha.4.tgz", + "integrity": "sha512-AF/o3AQLZjKWfk8mo7kKA2oI//mTKZcQvba85F/EODdGO4a3q/URc+KCIE3Xqx7b0iwEbyAE0ec4GVGSft57XQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-sdk-app": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-app-0.1.2-alpha.1.tgz", - "integrity": "sha512-hKnDVqRd76OvnRc+YPNbe8+HrU/aIk7S9zdKGlmnwykRuO6H/fo37EDzS3QKyc9stt8g/yFB9y3KxWOyrtLBUw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-app-0.1.2-alpha.4.tgz", + "integrity": "sha512-2dgLDJci9Qw5cn2V0mOYn1TAEFQ3+Y9xZQKaI32JX/Attj/QRyp3kXadpW5PlOkeUq7RMTMRU5oL9dB20MpjJQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sdk-jsonrpc-server": "^0.1.2-alpha.1", - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sdk-jsonrpc-server": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "commander": "^15.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-sdk-jsonrpc-server": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-jsonrpc-server-0.1.2-alpha.1.tgz", - "integrity": "sha512-BkPDpwH2O2jXGsR+1NVVEH539zASyfmlid4ElI6YFde88GG+HXXERXMlSWa9scY64mRYjCMTEgDxp70gkDh2Bw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-jsonrpc-server-0.1.2-alpha.4.tgz", + "integrity": "sha512-q1M04hYdlhMcaRzRfVLh9Pe/mz7oTx5GHi5Rfhru6ReBiApavUhUepb32/3K6Q8RIPebsirwG1hn0HCDuN1kRg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm-deepseek": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sdk-protocol": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm-deepseek": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sdk-protocol": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-sdk-minimal": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-minimal-0.1.2-alpha.1.tgz", - "integrity": "sha512-5IAhlZa+myXIvnkFEY+K+K79S4/b/V1XI8cBKbQvvxkhHQwN2e1JThMwKtuVv4LRivV+smmJD80AVZVqMbzv6g==", - "license": "MIT", - "dependencies": { - "@deepseek-ai/dsh-agent-spine-demo": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-deepseek-llm-api-extensions": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-fs-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm-deepseek": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-plugin-package-inventory-deepseek": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sdk-app": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sdk-jsonrpc-server": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-log-deepseek": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subprocess-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-terminal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-terminal-bash": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-bash-persistent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-pwsh-persistent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-str-replace-editor": "^0.1.2-alpha.1" - }, - "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-minimal-0.1.2-alpha.4.tgz", + "integrity": "sha512-WeixyytTSNQg/WeHeaBdSiWtOfmZv8ABD3eYpHhFGV6UmNf8O9vKe8wcwJjgNTMk2owgFc+WTrAZP2wyX4DCAw==", + "license": "MIT", + "dependencies": { + "@deepseek-ai/cordis-plugin-timer": "^1.1.4", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-loop": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-deepseek-llm-api-extensions": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-fs-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-jobs-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm-deepseek": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm-retry": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-plugin-package-inventory-deepseek": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sdk-app": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sdk-jsonrpc-server": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-log-deepseek": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subprocess-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-terminal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-terminal-bash": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-bash-persistent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-pwsh-persistent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-str-replace-editor": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-sdk-protocol": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-protocol-0.1.2-alpha.1.tgz", - "integrity": "sha512-fKCFRNVq6FiF+KCWJuZ9n8x+fPeDcDP09e+I/9KeyJDHS2Le+FV3NXcyuBQtLxls/02ev37gciEACvcazJ6P3w==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-protocol-0.1.2-alpha.4.tgz", + "integrity": "sha512-TT+bOh11tzPkT1/27LZO016HWTYdWoUSDC3pFAd9ZTyhHvPADP4XyWr2u68ragwqYay0kz8v/2Tij9ZcvvoPoA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-0.1.2-alpha.1.tgz", - "integrity": "sha512-UOssSFA5QthTDox8c/UJ6muUYeD8aIGXQ8/5Nsv5JpBuyfCODOUtcEhrP786OIKv08eeiRRMaZ7ik9IDSWFqIQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-0.1.2-alpha.4.tgz", + "integrity": "sha512-+0oNMzgeiFYErptVXEEQEY4rtj31oOEp8x6u3YI3oQyCYtEjztcqF+Rfhe2qw1WCLkNSa24XeRVlZ0yvnmGizw==", "license": "MIT", + "dependencies": { + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4" + }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-checkpoint-policy": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-checkpoint-policy-0.1.2-alpha.1.tgz", - "integrity": "sha512-QKR/kYHepmzLrI2dCQRHsf1IVsIRFSvO8CbqCcv2mb4sexQZKyUiGdFQ/aqL3hZEjMn7Vui6nMm2LYrUB0ofyQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-checkpoint-policy-0.1.2-alpha.4.tgz", + "integrity": "sha512-cZSarYBL7GRsrHews8kRvXSbAO40krMqp/zaa64IcvRtrV+KFzN9VWNm+UbgktQmatsdkcF/ZKHKKAs5DMKZQA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-log-deepseek": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-log-deepseek-0.1.2-alpha.1.tgz", - "integrity": "sha512-wxr206G1VEgWiek1MFUhX3CVpemO/r7sPP0NteLFSNuN5uniMJLgEYD/EFmrJztHJAVijYJgMHeeg3HtNnzV2Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-log-deepseek-0.1.2-alpha.4.tgz", + "integrity": "sha512-2x9spERyNogIzZYzqZGuaSTvnd39srHjTTBY9apsXPE69NWWUQgWXouxe20CCWujzY826Lkzi5gJgoHdNXwApw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-deepseek-llm-api-extensions": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-deepseek-llm-api-extensions": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-log-export": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-log-export-0.1.2-alpha.1.tgz", - "integrity": "sha512-yEhY0EkvUFMql4ZpviGBy5v1fSW8m5uaUugxEwtIXSALmlwANfiVI7OK38UANDmYV/VCOTDrpSoVHB7Q6OUkTg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-log-export-0.1.2-alpha.4.tgz", + "integrity": "sha512-KYUgAfrrHTdAEz8YFyMZyFfiyQX7F1JgE8P+nYPDngDlik1qUXjZy+37KHhTWY/HvO/fGs/r1tsQl/+D7sSqvA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "fflate": "^0.8.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-session-persistence": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-persistence-0.1.2-alpha.1.tgz", - "integrity": "sha512-4mdQYXwPss5GTlmgCoSFR07EjKZHo2CpubdMjrHMtuSf8rgAw9xr4E5COlceOvyGwYpwfeQ1e6cQL+cxmCKj9A==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-persistence-0.1.2-alpha.4.tgz", + "integrity": "sha512-lj/E3Qi2FecH0QUr6grR6PS6xpYNUTG5NAzpIXCuyKyOTR/HznpdL+1naPG91EQJG7Kk6Nam01qkBDYvtyAOvw==", "license": "MIT", + "dependencies": { + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4" + }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-persistence-jsonl": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-persistence-jsonl-0.1.2-alpha.1.tgz", - "integrity": "sha512-2cOSBDjOZby0PrVchAL51+YfuG6f24JBO+yk7FPlwHsQFYH/qus0hsl8CtgNZrqz/yxJcnir/WEkflVpWcgcHw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-persistence-jsonl-0.1.2-alpha.4.tgz", + "integrity": "sha512-ExGgiW/6AHG/NYhii9d92W24RZQ5b977Id9yKXsDfCLF2NyGO+1F7VHX69SXbHSEYvTg84tTeWivP5hoNsJdXA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "koffi": "^3.1.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-projection": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-projection-0.1.2-alpha.1.tgz", - "integrity": "sha512-ENjzlMoWnPW1B6nh4CTNIoSWr+Sah3E6quHb1cBEI2kKCdeigL0jjl1WXJ6QZcZbiyL46gJP+iEvByFq6w3s7w==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-projection-0.1.2-alpha.4.tgz", + "integrity": "sha512-bRUTmD7OwlIZeEeR88hxuNI4oD1NRFMxDedaDHCZi3m0xibEXm0IXhCO2JptD7LXhuehYhLthrSXff4qdtmdtg==", "license": "MIT", "dependencies": { "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-projection-cache": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-projection-cache-0.1.2-alpha.1.tgz", - "integrity": "sha512-x25t69kzItzW2yH1Mr4TO8SlEPRWYt2IwExRU2NVOBHO/w4s5xelpyQzxzA+v+U+eaHg+Tei0RdRcWX7PD1QDQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-projection-cache-0.1.2-alpha.4.tgz", + "integrity": "sha512-7+XS1+b+8QCscUfGms1FcPbhW2ELr4sTQzLA0/V4OgWp/AHEOfKDESXCGhlmA1J6M1X3OZdqk9Npz005k8TOWw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-query": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-query-0.1.2-alpha.1.tgz", - "integrity": "sha512-CzHweSPjqCCGG4hmHfcFV/cCqSuAqHFYOHIqJB+Zd5q8LHW4Z+B4Ts3A+muz0C4+qGO+IDD61Ks50U+nq2/15w==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-query-0.1.2-alpha.4.tgz", + "integrity": "sha512-v8zuyVio6IL9dDuR5fQYGdfEWo79xginnOVyveWObhGwc63QMmxIj8tSx+inlQJ0CKp0SzFGsKzd/QI+ETqjFw==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-todo": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-todo": "^0.1.2-alpha.4" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-session-persistence": { @@ -3798,19 +3404,18 @@ } }, "node_modules/@deepseek-ai/dsh-session-query-sqlite": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-query-sqlite-0.1.2-alpha.1.tgz", - "integrity": "sha512-81ijCur28XUdIDxZ/Ifc3iwbGqZcglDI64W2s4e90iBF/htVhN6XfrT8eEdbKtZhtuWPplfhiqSW7ouOnn5vnw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-query-sqlite-0.1.2-alpha.4.tgz", + "integrity": "sha512-16UmAhSQAvsXLDhUDdAbQ5ujDtuLIhldmsrc9MhqXwe48Kn2JAswez25mCx3NHXiBpn+U1PimEYvNNvi2YULEw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.4" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-session-persistence": { @@ -3819,27 +3424,28 @@ } }, "node_modules/@deepseek-ai/dsh-session-reference": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-reference-0.1.2-alpha.1.tgz", - "integrity": "sha512-SCTvuiREjxtqSnGkiOINWYsKlHZRe6Mh4X6RFfIKEFzkg+D2fqgi3xUaH4o4h7A5/qbLIN4mZAKAq0OjMDX0FA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-reference-0.1.2-alpha.4.tgz", + "integrity": "sha512-7JBk2eLY8eJ9nNawNn23PzXiEwOR7xZ+Tw7/IVUVbU4692KcaDsu81EB78VKXyfLH9iWkNcgUfmiLMhMQi4mGg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-output-retention": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-output-retention": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-session-projection-cache": { @@ -3848,40 +3454,38 @@ } }, "node_modules/@deepseek-ai/dsh-session-stats": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-stats-0.1.2-alpha.1.tgz", - "integrity": "sha512-BBEKm/uUkis0NXNUUAuCSFRHoTDLOXlkeR4Mhnwn/by87vBtS8bLhG4TeWigESyN71vzg2gMvCE7d6BFX4mhSw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-stats-0.1.2-alpha.4.tgz", + "integrity": "sha512-hM8V3ZFMhdGgtaAVTjQ2vw0wc4l2xur4gIuwDT4qQZD6jXQCsWUvlkAvuqTneeQ73Bk5fFH0vOZt+bdIQ7jR+g==", "license": "MIT", "dependencies": { "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-telemetry": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-telemetry-0.1.2-alpha.1.tgz", - "integrity": "sha512-pjti3EA7juyN8JhXXxZrxOK2Kq8NhCTl/RpSOfXKMUFBlo9xq9Dr4HCaEC/BcnU7rJng15SE/NWJJly0v32xkg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-telemetry-0.1.2-alpha.4.tgz", + "integrity": "sha512-ozJD6gdhI1yX9wvy96m8ldJWr+07YEuuAxCoqVCHGAV8DYXCa20On9CcZahlSAtxCrePKK9sxlCJHVfXrRCvXA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-telemetry-otel": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-telemetry-otel-0.1.2-alpha.1.tgz", - "integrity": "sha512-/vx2I7SWU/kOHAvtpyfKVTA8fh70AjpMO93twqXTCTO30srTPn1+vvzEdKUJ+WgyS4eQbLw6Y579N/eA6fGl9g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-telemetry-otel-0.1.2-alpha.4.tgz", + "integrity": "sha512-zS+LfKapyrz2WrF8UvEECqVKMQo+oN31epPIOrtsRS/8vGzSkXNVGBJ1P4C2sNJr/MVARtc2DXVXel3EnttN6Q==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "@opentelemetry/api": "^1.9.1", "@opentelemetry/api-logs": "^0.220.0", "@opentelemetry/exporter-logs-otlp-http": "^0.220.0", @@ -3890,170 +3494,184 @@ "@opentelemetry/sdk-logs": "^0.220.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-anonymous-user-id": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-command-feedback": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-telemetry": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-anonymous-user-id": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-command-feedback": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-telemetry": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-title": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-title-0.1.2-alpha.1.tgz", - "integrity": "sha512-9hed9TIrIHt/VjFqUGT8UK3y5dgNKJX0wtDQ1Tuj+UlST9ErCf5hVO19lm1UsviIz5a+Cu1KPfsWQvDIJqUKtQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-title-0.1.2-alpha.4.tgz", + "integrity": "sha512-mdbvRyMFv4PdFECv9wC2QPo3g4gwSeAJPFv7FjE5Egpe9mqt2Az8jLHPoOeZhIoYzy4oKiH8R82MHDp4ngTLrw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-title-first-prompt-llm": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-title-first-prompt-llm-0.1.2-alpha.1.tgz", - "integrity": "sha512-m6cFXdtExm0+eNvVePqgkTK1pgg3uPd+4WVhSckCj8FCxILHoOnQYzcEAJFSsvuwXAOsKmwUr3ENh2eAyPiPew==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-title-first-prompt-llm-0.1.2-alpha.4.tgz", + "integrity": "sha512-cPYk6U0aA3N5Zgkqer7oWtzqO/VSbKj7E2KdSk/qxCRWo9H/zXhsRMaRKZ2CVP2rKvnGXGMOf/oYCAjT0EFtug==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-title-llm": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-title-llm": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-session-title-llm": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-title-llm-0.1.2-alpha.1.tgz", - "integrity": "sha512-Ag8wb0uaeZcq+QwZ0dTDG507bScUg/3qjkhI1xuH3GjFo4slnlwcxxkR2meNWmXThDAAOh4jHQ8thfUd5thybg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-title-llm-0.1.2-alpha.4.tgz", + "integrity": "sha512-7E0gaH6LZEnKy2QGkXgzFNc5V6AMFVz510+m5DCEVgj7X6o7pON0OeI0EG7y6wj4ZRJBBTqj+bz2ELga/yu/AA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4" + } + }, + "node_modules/@deepseek-ai/dsh-session-turn-outline": { + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-turn-outline-0.1.2-alpha.4.tgz", + "integrity": "sha512-u3oVWV9eYMS9ZH+VdSsXWZtVTQdd4AeYTjRTh0bb5qrjTlMU6NdU0Cc56aO3KuvYg08lbzHAB+U12UO7Ud6+pg==", + "license": "MIT", + "dependencies": { + "zod": "^4.4.3" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-settings": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-settings-0.1.2-alpha.1.tgz", - "integrity": "sha512-yDPPSScZ1fGims3F/tTnt1BH15KU3U2qJzc+gLfVODnQ6i0q/bf6bK3ct+b2L94l8JaSMS/xR8L95ohephiKzQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-settings-0.1.2-alpha.4.tgz", + "integrity": "sha512-xWIjNVqnL6QbwULsneeWhchHjL8oIDrrJMZu5MOlhkReAXUoY+E83fw1+WfQ0DAAJQxgO/TVM4IcnFE2HaegYA==", "license": "MIT", + "dependencies": { + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4" + }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" } }, "node_modules/@deepseek-ai/dsh-settings-file": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-settings-file-0.1.2-alpha.1.tgz", - "integrity": "sha512-7H7Jb3kAkG8gMbO78kaB6bdQ6quR4Cd372puzOS5p23jnu4iKqEu9tQEo+JS8seofJpZnV6gH/HDZr77wZshNA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-settings-file-0.1.2-alpha.4.tgz", + "integrity": "sha512-rwU25AgRsptoQHqVPSB5R28px4EFqZqiSDWzPjhRRDKFUR6/fylyimt+7mQhh4zhI4Zr7ZusWeTije6exW04Mg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "chokidar": "^4.0.3", "yaml": "^2.9.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-atomic-write": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-atomic-write": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-shell": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-shell-0.1.2-alpha.1.tgz", - "integrity": "sha512-Qs2l4xwG5r4THuzMGOaE8vMVnw1GPthSVgPtmeeyOuxeo8XbO5j5CUaBQmFoAaqel79Nq8dsbQWaHWorPlTrsg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-shell-0.1.2-alpha.4.tgz", + "integrity": "sha512-c1QxrsWM0m83POeBBRc0cq5Jgwtr1LHsP9ubPuxFCoEnTDS1OAPJ/MYpxQBxyxhbIp4Uh8xZcdk34XDwm8uU7A==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-shell-env": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-shell-env-0.1.2-alpha.1.tgz", - "integrity": "sha512-XyvMDz5YyWjBFYTDdxXm6RhoQBLppG2GsUVsuDXozA3Vl8b6mf2XxwjHh4n20OLukJ42WjRrOY9dlJnuCdFlHw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-shell-env-0.1.2-alpha.4.tgz", + "integrity": "sha512-BrvY26hmC6ujyG9s1WShg1kfjKZ4Q3IIKzu6ofl/UPstjaEn/n0MHFx6YrAq29ST/C/dBNA6OEKpXiz91zvYcA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-skill": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-skill-0.1.2-alpha.1.tgz", - "integrity": "sha512-wP+4OSMgGHUAW7tJCSS+WSiNl8oqx0KFcw7fMnGa6xjAlaBbbLEA9t/gz1SrLgTf48MZO4b+NShCLYSpB4FLNQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-skill-0.1.2-alpha.4.tgz", + "integrity": "sha512-XgXtq3XGVcpxW2i0V7n6bJF3URcU6DDGkuh4Ipo+MUUkXl0gTXd4G07ZEtmT/3d7r9b1RZN5xqHNV13hSoO56g==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-skill-badge": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-skill-badge-0.1.2-alpha.1.tgz", - "integrity": "sha512-LhrQLsjnIiYcoDJmsW2cyA82jfz6yqJ0uNZGwftuCvq4MoJfvK0VqQyl3fkRXAWNqOSvE9kGWZJFzPABTo05rA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-skill-badge-0.1.2-alpha.4.tgz", + "integrity": "sha512-NjAJpljiJHYzs+NBcHRxLLk8rjZxgtp3PyDhgyZam8/lIekcuUN2G0rEM7r7BmsdN/CnWo/NmqUZhDrNBKaDpA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-skill": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-skill-filesystem": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-skill-filesystem-0.1.2-alpha.1.tgz", - "integrity": "sha512-f0/yD6q2I5Zz2U6XwUXVZJhYNJp4piAorgdLck/7ucDY7SMtShThg5OK+l5V4W1x9Uoc8AV4PO9jmCC1DTAGPw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-skill-filesystem-0.1.2-alpha.4.tgz", + "integrity": "sha512-dJQ5PuTiZfGl6CaZsYFVRf0VW4GF82389JW57KhNz/gdRHrCji2xs/dZsrdg0Vy/t3gOmmW75okSkKk/OR3xdg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "chokidar": "^5.0.0", "yaml": "^2.4.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-fs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-home-paths": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-skill": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-skill-filesystem/node_modules/chokidar": { @@ -4085,117 +3703,115 @@ } }, "node_modules/@deepseek-ai/dsh-spill": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-spill-0.1.2-alpha.1.tgz", - "integrity": "sha512-AZyUhmYd1MGFe91Ch4j+o8XBACNBW7o9tvhXLBlTd7n1H+5/RXo1tWDm0kgCaxgMZnvNJ9rmlTDHQ43Qiz0gWQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-spill-0.1.2-alpha.4.tgz", + "integrity": "sha512-vf2cnMUSFp0SUFAi7H9PGfh/axzQweumcyHQNiPfcGZTBDnK3niwyKySfHvYvuh6qahlmifzNTMRSXitWFYQUw==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-spill-local": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-spill-local-0.1.2-alpha.1.tgz", - "integrity": "sha512-/gKuo7fNLm8olpAILcz3//B8AnAwv4nETcIV5uL69Vf+k1X0mTJtrCzjvgYFKvlmT3Hp2BK73s0h85eFcaegrw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-spill-local-0.1.2-alpha.4.tgz", + "integrity": "sha512-5hTG+zt9+dEaP7UTLDpfggahbKaPBKVPpTJ40gy1nC0yNybENyk31XZ5OKZJUFIFT5FJZp9JgoMcN3kroasFpw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-spill": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-spill": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-spill-policy": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-spill-policy-0.1.2-alpha.1.tgz", - "integrity": "sha512-a+clPg17oaOktRwScU7MAEYt4fKerKI35S8WRmSakRC/wRr1TuShf9QoJJ6hkd7TBoQFikPhAVxpk/5Gt6GP0w==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-spill-policy-0.1.2-alpha.4.tgz", + "integrity": "sha512-Vwe1itGu/qWZE7YXP+rdYT4eOJAIOWtcYLVGUWtF5BgBmFMP/WEohVSvtZxiQUsjTrtjah7A41vcLZLLE2X17Q==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-output-retention": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-spill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-output-retention": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-spill": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-storage": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-storage-0.1.2-alpha.1.tgz", - "integrity": "sha512-XJK/afLkZCKECMKIZR4da4A9QaHfWuiWzpnqxV94KZIBX2+cbiDSEMEs9+T5gq3xOTDHFeb0Uh8jAW4Sp6/Odg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-storage-0.1.2-alpha.4.tgz", + "integrity": "sha512-4GhkPl2oQbVpsP7hvnyhayl49Hmd7RnGGoBby2pINkxAIKDboWA+6n+9v28aDKQbp5y/YfACjWYe70oX5bfULg==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-storage-domain": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-storage-domain-0.1.2-alpha.1.tgz", - "integrity": "sha512-TesWyk+bO80r04GZlVvcC2KE6QSJGav/2o7MZiXKu0u9zqySERMUpIFOWnRbX4gwkFLupr0QHrMT/2pfnWMdlg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-storage-domain-0.1.2-alpha.4.tgz", + "integrity": "sha512-nLlrtCG3TMJiSLUiLx0jrcf+vSU0utHrGgFn6xAMW5HQBQoHTZCNWGvtaMavCH6GFVp8sHhU3PL4GUucsoRaUA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-storage": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-storage": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-storage-json": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-storage-json-0.1.2-alpha.1.tgz", - "integrity": "sha512-GBNVwQ88lQXElpblvIx0TA8580S6t3biDyO8arlYO9O0A6X+sHzQtICkD9wOv+O9FUtytTypJbb67cxMMDXIlQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-storage-json-0.1.2-alpha.4.tgz", + "integrity": "sha512-D2Q49CU6w2S9vBPRQ46YDLzDHSlH3rd4OVIiU15xVi16aI0GMUlSSvO6D9TZbpQfijC7pYkExf7XbXrEj401wQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-storage": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-storage": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-subagent": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-0.1.2-alpha.1.tgz", - "integrity": "sha512-+MTfYsDKS3Z34LrHOLnV7f2qOVdtd4aig+gDM7vZ1zfLPG72RyHorYP/s1WatnTHUW15+SGqpAvHVy6AWIdOnA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-0.1.2-alpha.4.tgz", + "integrity": "sha512-0YyiB4Xt0Hjfo+AsEmDZ7/V7+gyZdHjMW6FrlyESx/YhAtJd2DcYBrmbl2hD1eku2FC13b8jvmp72ExbD/mmRQ==", "license": "MIT", "dependencies": { + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-time": "^0.1.2-alpha.4" }, "peerDependenciesMeta": { "@deepseek-ai/dsh-agent-presets": { @@ -4228,67 +3844,66 @@ } }, "node_modules/@deepseek-ai/dsh-subagent-fork-in-process": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-fork-in-process-0.1.2-alpha.1.tgz", - "integrity": "sha512-NQde4UXoj+lc+mECPK4QcY74lgy5VsrHAek/Y4HXpC+Ft1qucCSWQXT97UaHVLLVcUkYw6iNhkWZ9yDGJRQ7LA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-fork-in-process-0.1.2-alpha.4.tgz", + "integrity": "sha512-2u+UWb/5YtMFkwcFXKDQgpf3L9tAB/VVjqKNgvAzVK+p8jmiDNeQeaDnI8up1Ag+FF3Ul9J/6mPzHlUcv1/wPg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent-in-process-driver": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent-in-process-driver": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-subagent-in-process-driver": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-in-process-driver-0.1.2-alpha.1.tgz", - "integrity": "sha512-CHmyppqqj7QdcrFsNB8A5i3ozp8aWfKdTZ/hCkFa7Nuo2RCUwv/hMBNKzmOYBKCe9EN5/pg1y4JSU/RjQ8mXLQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-in-process-driver-0.1.2-alpha.4.tgz", + "integrity": "sha512-BhEH0Dpia2uynRI3ZOzUOAoTNQZ5D1eAfKHW982AOPkq2UdEWQ23xARE8iTAjYdLwwQ7/1unCIDTDbydIdpEwg==", "license": "MIT", + "dependencies": { + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4" + }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-subagent-spawn-in-process": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-spawn-in-process-0.1.2-alpha.1.tgz", - "integrity": "sha512-BHTQ3WVz0MwUQDDlWktMs0qYn2LoD20m1JPeaNRJZAq3cWGXcfrEXuc8ZA34m8xuZBOsvh+MRS8vbkODF0YY1g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-spawn-in-process-0.1.2-alpha.4.tgz", + "integrity": "sha512-b5NRBpAFFTyUhhTmTqlRNtWdemA+Sn4gcLDqI47HlptngwG8pq329G8hY4fs91gWfgiC79JJ4Y+3O8yFE/AJTg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent-in-process-driver": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent-in-process-driver": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-subprocess": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subprocess-0.1.2-alpha.1.tgz", - "integrity": "sha512-fBarvk4AJRll+okF19oBjxzfia4Q+u4HQ0wwA5L98FSrifC4icrXAOQcJSH0z8W62+Xo/Ptpu/v/vUIYR+Dz6w==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subprocess-0.1.2-alpha.4.tgz", + "integrity": "sha512-8snZNqf3TEPwd2Nsxt0+qR9Es4yENkdfic+7hXhrye+EW0uzgcksVbh3KnDY/ICrM3U9FnnsdLjKRxWSZ2nKWQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-subprocess-local": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subprocess-local-0.1.2-alpha.1.tgz", - "integrity": "sha512-5+lETohZW6aw6I1f1OAYF5iGwlerWcw+Z0AiZznndLQRgbbvUcMfdrWeZru4WDo4v3K7Cs07sNWcVVReRkJmBA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subprocess-local-0.1.2-alpha.4.tgz", + "integrity": "sha512-XF6ndiJ5GhqeQF4h4nMPlmlyxI7qvNwXDij9Yth+DszOd3+makpFzSdCFTqebvHjs7KFMydlxjgyhVqI0XfGbA==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -4296,521 +3911,498 @@ "node-pty": "1.2.0-beta.15" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-system-prompt": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-system-prompt-0.1.2-alpha.1.tgz", - "integrity": "sha512-133hsy+OPVqvux55KFfXN7i84h8hwx+aug/9Y4hCmgJdBouH0OaMuznjur0fmXe+QMCxiASZ0ZQ6Am5J6AdWTw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-system-prompt-0.1.2-alpha.4.tgz", + "integrity": "sha512-01Wuicn9tH0h3jW3WlngfeYGr/GFE1sMQHRsD7IiRWR3YDTp6PTjXLKDDkSm+eBoznJWkjkHaogFBJ0+9sblXA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-terminal": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-terminal-0.1.2-alpha.1.tgz", - "integrity": "sha512-4od+7qi3spcRcfQMedPct5YruwbjWj9yHFee37mN968z15esQ3gomDIxuhDHAP+gZZrhuS3Yo45xM/rUO04rnQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-terminal-0.1.2-alpha.4.tgz", + "integrity": "sha512-YjlEy8DHu4lANs3NZk3TjOhSRcR0q6ADKU6ThKUpNjkoM+Qn4TL8RmV3o9kLyPa1goCfDt+aKSdkKHS1li8iwA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-terminal-bash": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-terminal-bash-0.1.2-alpha.1.tgz", - "integrity": "sha512-d0JSLoCIpI/DsFzvVUApELa1/d44aV0NsBXQIcGkt/NLYn7Hni5gDM1ISolWAIzJca0xXETP4Oq5LI90iNyn/g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-terminal-bash-0.1.2-alpha.4.tgz", + "integrity": "sha512-is8PDygjuO7wP5c+zGdYtqWCK//bU+DZ4Oc0yknFiJyefKf/u4IlSCwBf5q6/jzbplyUaQKDveXM/TZLHvnGkQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-pwsh-local": "^0.1.2-alpha.1", - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-pwsh-local": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "@xterm/headless": "^6.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-terminal": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-terminal": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-time-context": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-time-context-0.1.2-alpha.1.tgz", - "integrity": "sha512-Y0ARpW4o66V3L4Zo/SidgTrifjIZ9fhp1Y9/jT99hRY+hgOOZoVxjE1xPl78171rd+WbOSLCUQaIx5yJ0klblQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-time-context-0.1.2-alpha.4.tgz", + "integrity": "sha512-6XSo3k893c3egh/WlSTIA5WMDGZmoWDsMLygYb3y8H1WCXtcXljAzL4XD8BpOlqiYd1ZhffCkbeSfifazjnJuA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", + "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-timeout": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-timeout-0.1.2-alpha.1.tgz", - "integrity": "sha512-+clO6ztCQB8ZtKYjMSxLiQBnwpZVgkuuPog+EckCbJ7c+zZfwvlBDOmxW9r2GeEAgZ+CoUd7A9PQLQD7NbPCWQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-timeout-0.1.2-alpha.4.tgz", + "integrity": "sha512-hPNasb/SSTpApOJyo1mIfJ9vqF9it3ldc9eYW8CT946loN+QuHwnMMGIBjIhcdYLOIXrA2pbKyNghNYP2GqmPQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-tmux-context": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tmux-context-0.1.2-alpha.1.tgz", - "integrity": "sha512-wUwZU6z2frwQ6bkPnlGHnjP2HETuLMl/qfE9jTeAJzFVqH+X/+J7Pr6YAUQRZSIfPIFOtRzveCwwoDhV5TxiSQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tmux-context-0.1.2-alpha.4.tgz", + "integrity": "sha512-A8WZTgVJj/lV3NRcZEtew+jFm0bgg0dYCustPlqzrUCsg5a/jAt2csN6HLpjtHhP4V244J7gldHP/C65vgjgnQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2", + "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-token-meter": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-token-meter-0.1.2-alpha.1.tgz", - "integrity": "sha512-gHY2Rgn+PCDyMz5WHiQ9+RihtkPmjIIYy6m1v8L2iEdexsasDYbqFsTeqSnu8x8h4nxIzkNlSMSiN7IC4M6lhw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-token-meter-0.1.2-alpha.4.tgz", + "integrity": "sha512-R3jUIisviYhLzqIy05ylEVVyh/T997dVn+DiH6DvPbdnjUfYnORLUB+Tv6MZvqgq1pBfx1gsS9+YcxGGUdzjnw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm-retry": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-compaction": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm-retry": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-ask-user": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-ask-user-0.1.2-alpha.1.tgz", - "integrity": "sha512-4UwcsdzIoSeCkSNuwljEVhVGggqao+wp9NMq9sjkLbMf5F9ZSoVz3nxkoswoGxYwnnRWcizfvLVONpE7xwhN2g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-ask-user-0.1.2-alpha.4.tgz", + "integrity": "sha512-IpHFZAoSJO3dFWpuLn3q4QdPQx2ysPmxzL3cE8optlrwWvQrAIOQCUMsase/pJq0bGePtfetamUgMbAlHi9cxQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-questions": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-questions": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-bash": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-bash-0.1.2-alpha.1.tgz", - "integrity": "sha512-Vkxw7p5ZwhMQOzw+5zl6cMltFpUGElDvYz2snjpsx8XqWp6wVCNPZ0++8etAghbRhPD2UuF6pWZpQgeZoIeTBw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-bash-0.1.2-alpha.4.tgz", + "integrity": "sha512-6U3e3tpa/hkYB8Tgsh0zMAswEWeIUEIOmQLwtHi2iAPIan5Q+BjerfUKCeXw7CeXj4XQMBMM96AI3hyV2Vy/lQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell-env": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell-env": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-bash-persistent": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-bash-persistent-0.1.2-alpha.1.tgz", - "integrity": "sha512-GZC6WWkHDaOTfZE7s6fqhA5PmTwg3QN5S5MJXUYzR6dAPJp/aWxZZXqsn4oGECd0bNCxfIChpnHY+fDiszCS5g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-bash-persistent-0.1.2-alpha.4.tgz", + "integrity": "sha512-KZMQkVeeHnzgNpoTkYh6N7I+I3OcmrS03KhHhEvTuWNCDsoh6qIEXryrR1L2xdl8/IBNi1vOPt2dYQxz6JRtJw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-terminal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-terminal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-call-timeout-policy": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-call-timeout-policy-0.1.2-alpha.1.tgz", - "integrity": "sha512-vXtGQV6rb1eIfjCLQyVpFQH93ky5azVzlYSMKOC50CQXMSjVl0niQ4q1tLAmlBxGVsEdTeDZ5NaTtcyej9Ddyw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-call-timeout-policy-0.1.2-alpha.4.tgz", + "integrity": "sha512-fiLY9Y/WocUwaFIQSCopfkQ6TO4ue6m5PJcclCGluEFKOA7s7vTI62hV6/tR4s9N2rqXI3R7Rk1PhKOVlVoBGA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-cordis": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-cordis-0.1.2-alpha.1.tgz", - "integrity": "sha512-v2OwiyM05TdiKfLuG0Fsgh8w2Uu9Ir2d+r3EFTkfP1ufb0fQWG0pwdnmXVR9/nhKmXV1ZvKTHT4D40NLsABPtw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-cordis-0.1.2-alpha.4.tgz", + "integrity": "sha512-u9q1q/alvIbKAfYIhe3CQ5kumck+6c9o+2xzfOyKpIE70klnO89WcwA7A/fnmnC2UdklVZ2bDPTnTwEx1d7ehw==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-cordis-host-runner": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-cordis-host-runner": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-fs": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-fs-0.1.2-alpha.1.tgz", - "integrity": "sha512-7XNITsDpPt5ZVNCIMFjd3D6yulVtDpKSQqwiJgzydbEYaoZndW3PjVdY92qIXAXeYkdY711PJONwP+rohDYv/A==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-fs-0.1.2-alpha.4.tgz", + "integrity": "sha512-KeUF01ax3iQNAgKDqKl7dAeJqf0hRUs7O4Qxr+w8jA3IQxwBII+5Kh7sTpHmAhqvYzE9UYQD/x1FiHKLSnHUQA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "diff": "^9.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-fs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-fs-search": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-fs-search-0.1.2-alpha.1.tgz", - "integrity": "sha512-RWqyI2909Z2lAoQVvc+X6wvV6jYDe4gaa+aWw4fhxxQf1SxU6+YkTLN+Z6lCDYWYGABhS+PlkN1uZS5EJMOYqw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-fs-search-0.1.2-alpha.4.tgz", + "integrity": "sha512-nEBDY5P3xcqvgQC6ctCrv1c+Vilmsy5q0cTkRItPQlhxdSyEzMGaVUTm3YQVLSXGgEzgemoUNW5g6rf4UiUnrg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "@vscode/ripgrep": "^1.18.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-output-retention": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-spill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-output-retention": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-spill": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-goal": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-goal-0.1.2-alpha.1.tgz", - "integrity": "sha512-SuJka7lmrVywFLtwraZm0lcuzG7ng1/dDSZuwuT7zlDXeJkOAOkCVFWQd8PKruj5JYLK+D75ZiAPG2F9c7v4sA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-goal-0.1.2-alpha.4.tgz", + "integrity": "sha512-fTP8PltC61j6zDR4ufWtwK8avl8NVFoYN+ngIMQW7oKxPnceG2qNbLRdDqcBx1VwocpZMhk2fFPsU+mwMKh5uw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-goal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-jobs": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-jobs-0.1.2-alpha.1.tgz", - "integrity": "sha512-p0jzWpHbrJ9IntoZYSi2kMNEM4hwHAG6IpViG9x4L/ie7zjy9yAHPjEL1RFwTsiQZFilMrjBTJ3Olq4GrplSUA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-jobs-0.1.2-alpha.4.tgz", + "integrity": "sha512-5ORyEOWVC8nX21KJdKoa5x3JYk2/S49OzezB/Tk1fAQAnE81Av3GwlSSdxajz0L+Bnn+84hp/OEhefD95Le7PQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-output-retention": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-output-retention": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-pwsh": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-pwsh-0.1.2-alpha.1.tgz", - "integrity": "sha512-WE4LkXBMZlCiQDIYfXm4sMclOhDtGrqLY+92WVi1CQ13Q1Bd0opgnvMwbgjBcvsE4usiQWF8AFhmqphBndl/Tg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-pwsh-0.1.2-alpha.4.tgz", + "integrity": "sha512-DFXxxcxDPqfSGcYbsbJQdM5g0HIlCmi2Mjaoc1Ts0vzfQGuoFinms+eKMVQ+6FjOjS9+JZe46XT8P+lRu2QE9w==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell-env": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-shell-env": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-pwsh-persistent": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-pwsh-persistent-0.1.2-alpha.1.tgz", - "integrity": "sha512-PoPJnH6NyfZXbqrL7sz62vvX1Cj/OYtCoo0K7v3LF/6F7oPzx1HN4GrzPI2mg0kb2ltKvqlAHZa2VO4gfWxpFg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-pwsh-persistent-0.1.2-alpha.4.tgz", + "integrity": "sha512-r9GSUSeMvtB0SXd/VMNI5wPnGb+5PXkeYSN+R/pPDGBm1XRB094nUxH/5hdvGB2LdHOlKrcgx3Bs+lzv7Qb+Lg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-terminal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-terminal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-ralph": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-ralph-0.1.2-alpha.1.tgz", - "integrity": "sha512-bpm4X4PS452rwYXU/Z5wEl/tIC5ANCtna55dk1EKL+lFXpoPb1Dan0po9N4XaspSpWxpRe+2bYpBMpAzkWsgFQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-ralph-0.1.2-alpha.4.tgz", + "integrity": "sha512-DrQYL85utCvcioGbsOvFtrVNI7DzJ6LBRtk+cLWInb85Bub7n5WxGERW91HDdwdTeY25Ve0GZ7OBFjuT252iGw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workflow": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-workflow": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-skill": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-skill-0.1.2-alpha.1.tgz", - "integrity": "sha512-CYYyHU8U/yo/4vJJouU7pAh2l2eiHIOjVjcw5xoP+e/E/6G0jOv/iGcQA7XwHqjO4XZCRXD3c3/lZm/DGj3SRQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-skill-0.1.2-alpha.4.tgz", + "integrity": "sha512-Veby3u9g89XmPhV1Bes+7qAtca2YM4C1Lg+XoEc3mbpj0OmZHbfLRB0k/xeFKpEtZ5kRgZFjRkGdyJhMYQ7B7g==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-skill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-skill": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-str-replace-editor": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-str-replace-editor-0.1.2-alpha.1.tgz", - "integrity": "sha512-L7kaUG3YMgUzSKmSD5dYyq1AvS9PbuB7M06KqA8+f4OKRpNwsYUgntzoSxsUnfB0C0CMCIYOblk6RPNXciU87Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-str-replace-editor-0.1.2-alpha.4.tgz", + "integrity": "sha512-v1+SQtZzC4PsC9iTs+H/KkT+rrXwRYYOzkCRpEWyqT07/LX5kmLn0vG06R4Fc+PS4cmJAl0ZLDYO45W3PeTKmw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-fs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-fs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-subagent": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-subagent-0.1.2-alpha.1.tgz", - "integrity": "sha512-neV+lAJK6WHnRAIm7gEnwJwWRthkxU08lbWiGj+qSbSNJNtJALpnksmMvwl0ZkfNXaEfmk8Rf+aLsOnLCyAEtQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-subagent-0.1.2-alpha.4.tgz", + "integrity": "sha512-tfqOOUgH8hR5Els7ula4na+DWyk1fuP7HCTjWkmJrC4TEcK29TZndMKTF1g3Dj/pG0eypTSmZakjpaH0lgl31w==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2", + "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-jobs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-subagent-control": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-subagent-control-0.1.2-alpha.1.tgz", - "integrity": "sha512-N/TFmTDk7SqfYP9kxYVbWtbW53TRT7f2Nk44vzcPVXTx/EOlZk6jcn/SJc7Nce5PDOZGQZ1tINTuEcqaWRFEqg==", - "license": "MIT", - "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" - } - }, - "node_modules/@deepseek-ai/dsh-tool-subagent-report": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-subagent-report-0.1.2-alpha.1.tgz", - "integrity": "sha512-/43tFlJ0CKqFUIz0fJZs+eo06xTiYvNDktHoyns9XSFAHn8INUp7sz+srxvXbikxhBEsFMu2paqgNX9WeDRhiQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-subagent-control-0.1.2-alpha.4.tgz", + "integrity": "sha512-fdh3TDjbF7okUm0rKUs3+cwrzC7a5RDhi4qGLdKzlNZO0klAUKbZP8kEgl2wkJxCgbE2+CssmVJL1Wh9JPCLgA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-todo": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-todo-0.1.2-alpha.1.tgz", - "integrity": "sha512-1gYnWN9dbspEx9B9i/b4DUX73VPbvFMESwhiOyLsRd9CS00vH9OP7IgdE2UQZVvskAq1yIGqIZ0i7S5iRiqizA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-todo-0.1.2-alpha.4.tgz", + "integrity": "sha512-SaCPo/xAsaXfwAE7yMe43vGuFdbnAV0p2GKnQU3jlgZMZ2rOM7M5j21jBashGoBYqBMjUeOyRUPOcScQmKAeZA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-web": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-web-0.1.2-alpha.1.tgz", - "integrity": "sha512-hVBg13dE2r2DZ8ek2iiEjaT+wHxZQq6XFlw9192QOEYHv1mp30N08UG4lYnf0Fg4f06qfeNbnpTd95ifq+X8vg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-web-0.1.2-alpha.4.tgz", + "integrity": "sha512-yDTwbnr0XnGlJWCIFl3EZ5Ri/M44R5Y85USb3hmdaoKdzLXDLgmKBm0QY8ZP0QKrw3+n2FpTLQh3SjJJmP3jTA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "@joplin/turndown-plugin-gfm": "^1.0.67", "turndown": "^7.2.4" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-web": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-web": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tool-workflow": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-workflow-0.1.2-alpha.1.tgz", - "integrity": "sha512-06kQAMZGrkb1LjNyOnWLF7hfoXnB9bzMqfhuv5dnwZomkMchvVHuXd0LLGhUHWHtoekAoNpRfOHkwVwu5CTStQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-workflow-0.1.2-alpha.4.tgz", + "integrity": "sha512-5ZtxuebfIm2agYVj2YdxIXCGtWRMBaf5HxD0QIIBHxGs0BKCgNRI1eNnqAQcp/cWHmHYJn6e/PtClfCRsInLrg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workflow": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-workflow": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-tools": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tools-0.1.2-alpha.1.tgz", - "integrity": "sha512-dSzYhTum6OJfJ9koR/3tDo+2diuvx2Dzqy5ql7zXF5NHFaEA08Bujly8ekkzpFUtqYOPocMNNLaHftghqBmD4A==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tools-0.1.2-alpha.4.tgz", + "integrity": "sha512-NbzgFxFyrq5Jh1B7m5/gXSHrvKWmOQ0BVvSItz9PaIb9VVjIKcedrdzOu98j3OwjxPUfKeTnUkZfvX1VwJX+rg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-code-runtime": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-code-runtime": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-typert-generator": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-generator-0.1.2-alpha.1.tgz", - "integrity": "sha512-8LiE/hYcJAwstmIl1alMZ4hdSH6VyfCQubANj/z+sZWwCEh+htsQ7/Mv/u245skAMWFQhYvwXcMMSn4sIsWfQw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-generator-0.1.2-alpha.4.tgz", + "integrity": "sha512-lWl/HCYWfWetpwNtozRl5OuaesrrsBiivVYDdoQSUqcqU1uCIQ9rYQG+ePfVuqQDY6NYDbeXaERCuzPJ/GWpFA==", "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.13", "typescript": "^6.0.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-typert-generator/node_modules/typescript": { @@ -4827,337 +4419,349 @@ } }, "node_modules/@deepseek-ai/dsh-typert-loader": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-loader-0.1.2-alpha.1.tgz", - "integrity": "sha512-d1j8aYuUp1y1ZwWnCJwdTzR3D0yiK7Fx0e40LtW6xyqXbQrI7/z1v9nouXgxpQoyi+DV//XxmVZiPR+Nto5/qA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-loader-0.1.2-alpha.4.tgz", + "integrity": "sha512-RrpV9LV7fFV353nQkuur8xdUC3EStp0E7pgtLc8Nb8QG2DrJjaw3kesBH05c1hA69A9wdODlj77Nb+tfSUTwlA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-typert-registry": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3", + "@deepseek-ai/dsh-typert-registry": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-typert-protocol": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-protocol-0.1.2-alpha.1.tgz", - "integrity": "sha512-a/N+i849kXh2QTl4AaxX2H0848ukx+fl/FuPWjpf9gAhg2A7H2kw1xn2KiHV9gRyTYCgYPOG5gHvIJk1nSh4rQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-protocol-0.1.2-alpha.4.tgz", + "integrity": "sha512-YFwMY1D18qju4zCA7mEYTKAmanLBbtL2DLVCY62Z0FAWkz3fSYul8UGHuvo853QDUh4rirk01Pr6NciCJFWo4A==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-typert-registry": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-registry-0.1.2-alpha.1.tgz", - "integrity": "sha512-gTuZ3JbeOgAYLKdXGkQawGTGugNkj3v2oVMKTWlyimTeYE6tRaYSGj+Ct+2xJOaBNETjhIunuFoCooJbYo7xOw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-registry-0.1.2-alpha.4.tgz", + "integrity": "sha512-Bl3UJEj/qyS1eWCh0JhPXemRYND6Q4ocBmOW+RD+C6hm/naI0DLYjRoUzQM1Yq62oRTz6L0KoZv0ANWoR+28kg==", "license": "MIT", "dependencies": { - "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.1", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-user-approval": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-user-approval-0.1.2-alpha.1.tgz", - "integrity": "sha512-e3BTPSnnHkYhXQ3qoWrJGTutf5PRztX9XTXD07FHHPGw8j7Q0pwPbGfwecr8GLFN2SYk2Ore76PKKmZIhiSbTg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-user-approval-0.1.2-alpha.4.tgz", + "integrity": "sha512-eoqJYt4irBrER7aLuVpUVKQ8+pBqTIBNwPecAhwgh4L0uMhTe9X+oncmg8dW/okDX88SNPCpK6mP1eRsmSewLg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-user-questions": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-user-questions-0.1.2-alpha.1.tgz", - "integrity": "sha512-qs+KxccTItSGHC+Gcfp5/IkP6GWDblcCvwk4Dfvjs2Igx7HVEldLZUOckdb+mZW4KUJnX6QaUYmQD6LLuPTFIw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-user-questions-0.1.2-alpha.4.tgz", + "integrity": "sha512-aP93bbwMnCuqWnf6ra0vyG/6kJuw80MBVB3yH9dToWSIzanJz797N+++fjlaB3QYnL9Q770tjB1C85/GE6HKxQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-scope": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-scope": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-util-crypto": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-util-crypto-0.1.2-alpha.1.tgz", - "integrity": "sha512-C10gFPfOzSKO3SO5ZU6Gg2kERQayd9d2CqgSldhrl/8fMngxQ0D8FAzO1BF71iTSXSeMeLlv9A/7RDo+NBN1Qw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-crypto-0.1.2-alpha.4.tgz", + "integrity": "sha512-NIhZCxGkze/JTq7htqUvnbfL1GceZbl/6RrwNsVmTlrtLgoVwXhaFsDSbSRJO2wgBsMJTXjgDf6AoPYEcELYtg==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" + } + }, + "node_modules/@deepseek-ai/dsh-util-time": { + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-time-0.1.2-alpha.4.tgz", + "integrity": "sha512-VM+MncUC3KQn4dCQg1fkBKRgryOvPdxDW4UTowUvQvpxMUrjuafJfFHd/iqsvfhjavJgDgMS7eo4yqCrBxiG6g==", + "license": "MIT", + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.2" + } + }, + "node_modules/@deepseek-ai/dsh-util-values": { + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-values-0.1.2-alpha.4.tgz", + "integrity": "sha512-A42TvqZqNYM6s3FkI347y6OPGmqFIty1BFV2nYfFWPavIb4wNoy0W5as6blLN2/LCSA3jnx+Ri3wTmAsqTTOpg==", + "license": "MIT", + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-util-workspace-path": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-util-workspace-path-0.1.2-alpha.1.tgz", - "integrity": "sha512-RD99f1185FFzpKR0iUUoF+OB9uwhirm1LvMpiUdp7bx4ZQSmcz0RTlvzg2ea8LwTwjyIsnuUdVQggdLehVdNtQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-workspace-path-0.1.2-alpha.4.tgz", + "integrity": "sha512-RHe2tOqbRiKx/AebI43+Q9mW42mYcyxU3rRYNsqINg+iEypb7/D7/yQ9Z2rQfwt6uM3EXeuGgI/45QLR+TJCeA==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-web": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-0.1.2-alpha.1.tgz", - "integrity": "sha512-PjFot7YYs4OUTBU0LxS1vNjyFN4a0LYtBMPA+zfU9s+yeAfBC0n2qDfOOIHys1rY7vg+yE2Os/sOkCw49/wtmA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-0.1.2-alpha.4.tgz", + "integrity": "sha512-XkRWQDw9iO071X0CTZE2p2/2x5xiyJOPc2dmMjKWhKzLYI7iA70r6zmPS59lLOSSN0cuJ5iF9zypHcxf4tc+oA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-web-app": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-app-0.1.2-alpha.1.tgz", - "integrity": "sha512-7yzS9EoN4LcmmfJ0OzPzDrC8zgL41iGS281j3lm5X01mS0/Lr85HhDOm27kHDiCZm5i1HnMF29BJt9yUyxTVrA==", - "license": "MIT", - "dependencies": { - "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-settings-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-app-boot": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-hmr": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-modules": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-agent-preset": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-approval": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-attachment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-brand-official": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-commands": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-cordis": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-deliverables": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-directory-picker-browse": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-directory-picker-native": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-goal": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-jobs": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-layout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-message-feedback": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-model-selection": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-permission-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-plan": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings-general": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings-models": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings-plugin-inventory": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-skill": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-theme": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-tool": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-trajectory": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-user-questions": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-workflow-run": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-workspace": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-cordis-client-runner": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-cordis-host-runner": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-file-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-file-reference-local": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-directory-picker-auto": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-directory-picker-browse": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-directory-picker-native": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-frontend-static": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-plugin-inventory": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-message-feedback": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-log-export": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-reference": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-stats": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tool-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-web-frontend": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.1", - "@deepseek-ai/schemastery": "^3.18.1", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-app-0.1.2-alpha.4.tgz", + "integrity": "sha512-t9+XEZWPKBAlW4heKK2thOgawgSnlHPus5GAIrwc/LsElnzRbZFhjwqhYATCbGkbvBCQS7zMWWl7+VvaIfsI+g==", + "license": "MIT", + "dependencies": { + "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-api-settings-controller": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-app-boot": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-hmr": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-modules": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-agent-preset": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-approval": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-attachment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-brand-official": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-commands": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-cordis": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-deliverables": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-directory-picker-browse": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-directory-picker-native": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-goal": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-jobs": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-layout": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-message-feedback": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-model-selection": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-permission-presets": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-plan": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-reference": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-schedule": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-settings-general": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-settings-models": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-settings-plugin-inventory": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-skill": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-theme": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-tool": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-trajectory": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-user-questions": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-workflow-run": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-workspace": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-cordis-client-runner": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-cordis-host-runner": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-file-reference": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-file-reference-local": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-directory-picker-auto": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-directory-picker-browse": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-directory-picker-native": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-frontend-static": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-plugin-inventory": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-message-feedback": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-log-export": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-reference": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-stats": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-turn-outline": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tool-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-web-frontend": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "commander": "^15.0.0", "open": "^11.0.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/cordis-plugin-loader": "^1.0.2", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-shell-env": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/cordis-plugin-loader": "^1.0.3", + "@deepseek-ai/dsh-shell-env": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-web-fetch-http": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-fetch-http-0.1.2-alpha.1.tgz", - "integrity": "sha512-uUfkk/IaigUlEpn+KploiHiFbhH6CE3nP/NgIeOwRKwI9h80ahAGKmPdXLatftv6tKG1E2Zi+nJ/UWFl1zTWXA==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-fetch-http-0.1.2-alpha.4.tgz", + "integrity": "sha512-4QdFFi3xJh6t1HbVw1eIGA19yw2imUHG338arhofxLg4zblcMBGvGtXDyYrW9cFVCde3vd5B70BXslRnBK7ydQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/schemastery": "^3.18.2", "ipaddr.js": "^2.5.0", "undici": "^8.10.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-web": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-web": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-web-frontend": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-frontend-0.1.2-alpha.1.tgz", - "integrity": "sha512-63SduG3yMTtPyUYmJdvVgjn4ecVERDPup9hYEnZNO0sn9Vdg7/zVbfNbkEiF5kEMPtc9lVddMHtrzuvfdUidoQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-frontend-0.1.2-alpha.4.tgz", + "integrity": "sha512-IflFWPQPUs6YhEFjWWrk4xSRc+rqVroQ4sfc2kLSIynK2hyBJ8HhfJpj4bcu2Dwg2vyjYpH85RMRpG4XYKi2HQ==", "license": "MIT" }, "node_modules/@deepseek-ai/dsh-web-search-deepseek": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-search-deepseek-0.1.2-alpha.1.tgz", - "integrity": "sha512-Z7+FrciuVd5XFDrtG5+TQyg1MhThHNT+Pj8110jT+XtBiyhtu3IGGydbKDQA/cUYaMuSuLwQyNjk4QI2S6MdGw==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-search-deepseek-0.1.2-alpha.4.tgz", + "integrity": "sha512-X6AovEqzrKViQyrmSjSzoYvm9b+fI2q9kuBjkC+aokEVhD7vq1LxYkng1kk0tg69geukEpYGXOFIzuf16jPzaw==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-settings": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-web": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-launch-environment": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-web": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-webhook": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-webhook-0.1.2-alpha.1.tgz", - "integrity": "sha512-SXKTN/OvR3zLdLfOmhYorzHBt30RZY4tVMneRaBinfIDYo3gRxh+JbK7C4plmuxiaJ3zYmJHQB0K0iUr1kpjOg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-webhook-0.1.2-alpha.4.tgz", + "integrity": "sha512-kvz/FgBLTlnmj85k7T5BJ4X+VZLxDGmtstQxSVyo9d9fdaXICN64acCk+M+32lrkFTM2IXkx8WfDTrgo7/nt7A==", "license": "MIT", + "dependencies": { + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4" + }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-default-model": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-permission-presets": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-default-model": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-permission-presets": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-title": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-workspace": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-webhook-github": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-webhook-github-0.1.2-alpha.1.tgz", - "integrity": "sha512-gy/l+NmAvcS0HvQS8rQzf3jD2IWsv4ikhH4A/aZDXSCDCW7DiRqKT9+QQqlhSnfq6vzDnv9vSV8u4YlHwvuJLg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-webhook-github-0.1.2-alpha.4.tgz", + "integrity": "sha512-3gPcSo//0onwyqJ0bDic8QSA6QftwZHIkLq53QpEdVlqGRZ42wcZYfmKV3EvF/F1g8951/spFnbDFGwZbHkfDA==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2", "@octokit/webhooks": "^14.2.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-webhook": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-credentials": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-webhook": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-win32-process": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-win32-process-0.1.2-alpha.1.tgz", - "integrity": "sha512-Ox/SMsXU0E7V3juKRFqffY5dxypt+GG47NXdy/1NY+swQMZXiStXl1Lu5fIuOU4wRCm7lskpjKdLALVMLVGnoQ==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-win32-process-0.1.2-alpha.4.tgz", + "integrity": "sha512-BYtQC4ELUTZI4I927DaGE//HJkAVIyrFF8gyULmzuabt1qW7cmITcONwITvRMMjE8zczEbApadHYc0nTblIMOQ==", "license": "MIT", "dependencies": { "koffi": "^3.1.0" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2" } }, "node_modules/@deepseek-ai/dsh-workflow": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-workflow-0.1.2-alpha.1.tgz", - "integrity": "sha512-HWYOv3Gpy2BVOSgjuNR4aTwBQIL0mpS6XyyeS/SzhR7FhSv+ngLo/PKJGFNo+QZ4oraHPEPlZaEKMg3NkaVk2g==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-workflow-0.1.2-alpha.4.tgz", + "integrity": "sha512-WwBFrMt4x0xKe7HlRNQs79+J3mI7USHj0CapWwJFcsHYN9fXjSxqC7lOuCo+O5iWsBg+hMg20K3bdi+zVj64VQ==", "license": "MIT", "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-workflow-worker-thread": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-workflow-worker-thread-0.1.2-alpha.1.tgz", - "integrity": "sha512-7HDFEKqFlziFWfbaVmhFfar2GwHmaqWKj/B29n0A9Oab+5g5BjKzyMVRLA1C/NF9Acgs9xBkkQjFKAd2kCX/8Q==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-workflow-worker-thread-0.1.2-alpha.4.tgz", + "integrity": "sha512-CkAIQiEF2ztzan/dZyKQuBmHXRct8wsMk04AnW5UybVVekFPnIl+hiz5J6fbWscyL1lU/9mew0CyTd6XdYyNJg==", "license": "MIT", "dependencies": { - "@deepseek-ai/schemastery": "^3.18.1" + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.4", + "@deepseek-ai/schemastery": "^3.18.2" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-llm": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-tools": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-workflow": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-agent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-llm": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-tools": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-workflow": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/dsh-workspace": { - "version": "0.1.2-alpha.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-workspace-0.1.2-alpha.1.tgz", - "integrity": "sha512-i3piIvW80wd8gRbWXva2yq3XLUKdRjtQWts/YDlg2nn4eqtH6DghrYhkRl0x635vIpUnFNh0TBc79cuYGylBTg==", + "version": "0.1.2-alpha.4", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-workspace-0.1.2-alpha.4.tgz", + "integrity": "sha512-nC01Fc5S88cl2GlC5nMw5BOzQNKAa3m4xyH5KW3psOQTFyndy+HC1GFE/8JgSsFTWim1QWV9JyI3n63iZGwEug==", "license": "MIT", "dependencies": { + "@deepseek-ai/dsh-brand": "^0.1.2-alpha.4", "zod": "^4.4.3" }, "peerDependencies": { - "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-brand": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-storage": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.1" + "@deepseek-ai/cordis": "^4.0.2", + "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-storage": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.4" } }, "node_modules/@deepseek-ai/node-addon-landlock-run": { @@ -5206,24 +4810,24 @@ } }, "node_modules/@deepseek-ai/schemastery": { - "version": "3.18.1", - "resolved": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-schemastery-3.18.1.tgz", - "integrity": "sha512-+nwx6ai5x3P5t2e0HLZbVEcpTh7t0o26rejTMF9ZLReeK3ULEDaBBnOHOAc3qjqQV449WW5WVJXfta6B7jtLhQ==", + "version": "3.18.2", + "resolved": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-schemastery-3.18.2.tgz", + "integrity": "sha512-wVqW4SPhlktSKhsi3WA/7PSF0bRsUvZmRPkioClzuirlJdFGbFlTciivQDNeKxq220B6mzJY7CGRau63XcUyTQ==", "license": "MIT", "dependencies": { - "@deepseek-ai/cosmokit": "^1.8.2", + "@deepseek-ai/cosmokit": "^1.8.3", "@standard-schema/spec": "^1.1.0" } }, "node_modules/@earendil-works/pi-ai": { - "version": "0.84.3", - "resolved": "https://registry.npmmirror.com/@earendil-works/pi-ai/-/pi-ai-0.84.3.tgz", - "integrity": "sha512-M0YUV8vNO3y2WwWSyY8ijKJV5W4gkSUixuvk+Z00ZBjsyMfsdXfITsHEwP1UIf09YRWXT6oGn0GlCamt+P32XQ==", + "version": "0.84.4", + "resolved": "https://registry.npmmirror.com/@earendil-works/pi-ai/-/pi-ai-0.84.4.tgz", + "integrity": "sha512-AClAZxf5+c4RRu44NJPS6wyQy+Nmq+Mzyyrdvm4ZVMNuixelO02RZX4G4Aq1F145Yzp43wnM5S+hLlSI7ypfVw==", "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "0.91.1", "@aws-sdk/client-bedrock-runtime": "3.1048.0", - "@earendil-works/pi-telemetry": "^0.84.3", + "@earendil-works/pi-telemetry": "^0.84.4", "@google/genai": "1.52.0", "@smithy/node-http-handler": "4.7.3", "http-proxy-agent": "7.0.2", @@ -5233,36 +4837,16 @@ "typebox": "1.3.7" }, "bin": { - "pi-ai": "dist/cli.js" - }, - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/@earendil-works/pi-ai/node_modules/@anthropic-ai/sdk": { - "version": "0.91.1", - "resolved": "https://registry.npmmirror.com/@anthropic-ai/sdk/-/sdk-0.91.1.tgz", - "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==", - "license": "MIT", - "dependencies": { - "json-schema-to-ts": "^3.1.1" - }, - "bin": { - "anthropic-ai-sdk": "bin/cli" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" + "pi-ai": "dist/cli.js" }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } + "engines": { + "node": ">=22.19.0" } }, "node_modules/@earendil-works/pi-telemetry": { - "version": "0.84.3", - "resolved": "https://registry.npmmirror.com/@earendil-works/pi-telemetry/-/pi-telemetry-0.84.3.tgz", - "integrity": "sha512-sgEkWoKrvSGaKn+YfLLFZmn+/A7B/w62eLwTD57nI+C9to8ITlFFVbgC2OtwvPnT3NFGHdCd53qhBEMIlptD1g==", + "version": "0.84.4", + "resolved": "https://registry.npmmirror.com/@earendil-works/pi-telemetry/-/pi-telemetry-0.84.4.tgz", + "integrity": "sha512-8e2CuxM+ht+hedQXTZmi5JVl6/xDK9RpSDL2+MbITevKYQhMZ/z6lJOTFgox3HQyGxO8mOZEtYGVeQNaD4OzqA==", "license": "MIT", "engines": { "node": ">=22.19.0" @@ -5324,28 +4908,6 @@ "node": ">= 6" } }, - "node_modules/@electron/asar/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@electron/asar/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", @@ -5374,39 +4936,6 @@ "electron-fuses": "dist/bin.js" } }, - "node_modules/@electron/fuses/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@electron/fuses/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/@electron/fuses/node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz", @@ -5423,19 +4952,6 @@ "node": ">=10" } }, - "node_modules/@electron/fuses/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@electron/get": { "version": "5.1.0", "resolved": "https://registry.npmmirror.com/@electron/get/-/get-5.1.0.tgz", @@ -7530,9 +7046,9 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "28.0.0", - "resolved": "https://registry.npmmirror.com/@octokit/openapi-types/-/openapi-types-28.0.0.tgz", - "integrity": "sha512-0rFyLuyHvIj6uuZWuDslxkowFYdPXoNIkeAv4b27dzm2Tf4vGWXnPsMcxs7d65kLdMERgP3wc1AEPlqMz8e1cQ==", + "version": "29.0.1", + "resolved": "https://registry.npmmirror.com/@octokit/openapi-types/-/openapi-types-29.0.1.tgz", + "integrity": "sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg==", "license": "MIT" }, "node_modules/@octokit/openapi-webhooks-types": { @@ -7542,24 +7058,24 @@ "license": "MIT" }, "node_modules/@octokit/request-error": { - "version": "7.1.1", - "resolved": "https://registry.npmmirror.com/@octokit/request-error/-/request-error-7.1.1.tgz", - "integrity": "sha512-+eaY7G2VVpSf2pc5Gn1+mph837V/d/TYTJAgWL9Tb0ogGYcpN3IlAVFgjL+Vv93F/sevrxkvsYCedtpLdcFLzA==", + "version": "7.1.2", + "resolved": "https://registry.npmmirror.com/@octokit/request-error/-/request-error-7.1.2.tgz", + "integrity": "sha512-XZRuT3xZ84D3gYErI1DZvhJ33dCWVV6uzBtWkaBB4TvA/L6eOeTZodxLFVB44bBEEo3vEx7y00UfX1tBLrtLRg==", "license": "MIT", "dependencies": { - "@octokit/types": "^17.0.0" + "@octokit/types": "^18.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/types": { - "version": "17.0.0", - "resolved": "https://registry.npmmirror.com/@octokit/types/-/types-17.0.0.tgz", - "integrity": "sha512-ByP1v7YL5SMveFPP7+sj0/ZuWCOOg/Chs4NafOMpq6WNIM/hdGY0S7C0TCGDBWu1aGmOxmUIhMx3cO+IdwYZ1Q==", + "version": "18.0.0", + "resolved": "https://registry.npmmirror.com/@octokit/types/-/types-18.0.0.tgz", + "integrity": "sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==", "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^28.0.0" + "@octokit/openapi-types": "^29.0.1" } }, "node_modules/@octokit/webhooks": { @@ -7693,12 +7209,12 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "2.10.0", - "resolved": "https://registry.npmmirror.com/@opentelemetry/resources/-/resources-2.10.0.tgz", - "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==", + "version": "2.11.0", + "resolved": "https://registry.npmmirror.com/@opentelemetry/resources/-/resources-2.11.0.tgz", + "integrity": "sha512-Ie7+8q8MDF4FAEQCKVMTx3ReUvxiIAgIiiW3c9JdmP8+HMcDy20puT+AHjexnExgnbvBxjQ9fjkFDWrikJ2jQA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.10.0", + "@opentelemetry/core": "2.11.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -7709,9 +7225,9 @@ } }, "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { - "version": "2.10.0", - "resolved": "https://registry.npmmirror.com/@opentelemetry/core/-/core-2.10.0.tgz", - "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==", + "version": "2.11.0", + "resolved": "https://registry.npmmirror.com/@opentelemetry/core/-/core-2.11.0.tgz", + "integrity": "sha512-7YP44XH0tV6+Mb54x2YGf84i7yi+31MBZlE8JwvozkxyTvXbSp10X7cI7YE49ChJ3shMJoBmCJF3+1QFBJctGA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" @@ -7954,9 +7470,9 @@ "license": "BSD-3-Clause" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.0.tgz", - "integrity": "sha512-70TeIFezKKy65LgAVyQh+w94/gjWhvPWaLaGGeMEgVrPkQhuj/M5bAYYZzIFUj9Y69oHyTm5Um/R6gcLh4A8JA==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", + "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", "cpu": [ "arm" ], @@ -7968,9 +7484,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.0.tgz", - "integrity": "sha512-YC86tYIHK6M1IV+wbzO+Bxk8RCBr6ZyWYgWxUCzaZD8mc8rrFoIJDNzDrkHBYRc/wKdrsIXmm6/F7NzrAO+OrA==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", + "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", "cpu": [ "arm64" ], @@ -7982,9 +7498,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.0.tgz", - "integrity": "sha512-oI+ECtUcli0y0fi4xpW82GdPIXdTkI8G8DSjG2LRuw09fPAGykaWYH/hXxiKuTxiAjiPSTIIuYUqof5Z2hShWw==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", + "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", "cpu": [ "arm64" ], @@ -7996,9 +7512,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.0.tgz", - "integrity": "sha512-NwV+1s7TiKrMe4owHyKB/dTLD7ZJD0YEBEhIz+hvav1Cu1GReJjF+rsdNwjzENQeIAbE/CoNiaAc5Vz2h5DPAA==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", + "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", "cpu": [ "x64" ], @@ -8010,9 +7526,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.0.tgz", - "integrity": "sha512-tWtHBTu5gOPK4u4Urtk4qAHW3zZ9rQAmbssO8gp7ELvGTGI3aCiq6NqyTQ0PCIg7KbHJF2UkGDDs77YZGxfjCA==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", + "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", "cpu": [ "arm64" ], @@ -8024,9 +7540,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.0.tgz", - "integrity": "sha512-2qPoJiwTvtHQ27NnYvTnsgk8laXWYuVmNESG8WFZBcEPKLfZ3I27qBJarjVRQtwGeYyRfq5ZowHXih9lm2BItw==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", + "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", "cpu": [ "x64" ], @@ -8038,9 +7554,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.0.tgz", - "integrity": "sha512-FQwsTRvLNuHoTdICABJQfbPUSEueISGmnpT06tXTMpfprf5NiKLSXKA0A+w45wJnCmZAnzgqBwbt6ARFuyOi5w==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", + "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", "cpu": [ "arm" ], @@ -8055,9 +7571,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.0.tgz", - "integrity": "sha512-BBVTXziw8mY1a4ZbWME9tZyfzqXCDPqaC7Z3heQ29p5dkvXzwL0NwelO8zLa8c3RBKvl3YTuSnBgsBhYBtwjIw==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", + "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", "cpu": [ "arm" ], @@ -8072,9 +7588,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.0.tgz", - "integrity": "sha512-w2Iyy9+RqKwx3d9qWMKsJg0FfRBsY0/pXNv0mCQ3ueRvJI6+QAScfD4nrMlzFLs2HNVW6Ew+mtZfDl9b7Ew5/Q==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", + "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", "cpu": [ "arm64" ], @@ -8089,9 +7605,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.0.tgz", - "integrity": "sha512-YK++KtrFRHYE0P6/RtYEAy9t8F37znP+K03RrIuLPYOL6SVlObRumf/0OE4V/h63xL9DwkWbNssZfmA9hawuDA==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", + "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", "cpu": [ "arm64" ], @@ -8106,9 +7622,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.0.tgz", - "integrity": "sha512-aBfOG6fP7YkkPmTqPwufRJeFyz7WPpECv9XNbnsk9+vg7rxdih0lbtEel7jcRng4LZrrmU3FfitCFyEj4BWDWg==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", + "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", "cpu": [ "loong64" ], @@ -8123,9 +7639,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.0.tgz", - "integrity": "sha512-LGaHEOeHNAag9VuS1Crs5DFg4RrU9MPi2nVnNJk9DTePx/B6RRYKVmrIXt2h7YOJlwjaFJ6lwtFDliZxScTLrQ==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", + "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", "cpu": [ "loong64" ], @@ -8140,9 +7656,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.0.tgz", - "integrity": "sha512-jClvk+J0FC3b7Udvegiw5/4hErbHtmsNsQgENnKXDWtNCJXsJYZH5WURvu7imDOO38xYml24eeh5x3A04ppwCw==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", + "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", "cpu": [ "ppc64" ], @@ -8157,9 +7673,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.0.tgz", - "integrity": "sha512-0OJlaGK+8+B777Ql5okIpD7ua5Ro9+VB9Ve0OKa28OQJZ1RbuUBVNHK/e3pr4BROqsyPl1JrPO1ZxJseCNffcA==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", + "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", "cpu": [ "ppc64" ], @@ -8174,9 +7690,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.0.tgz", - "integrity": "sha512-Ygsx+HoNH7afwi1bTIXbnTvVnsO+zurPLSYxybV1hHFVU72OWOCl6v05ql/z0hkpAPx+DK7Kn9Bi7MayCcjLTA==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", + "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", "cpu": [ "riscv64" ], @@ -8191,9 +7707,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.0.tgz", - "integrity": "sha512-pDQxtMGb+OvG3fLwR2OkZlSd47hW+kWg4BYMG/++sR6RqorQccwPTDsxda5hPwiIeIErAnCF9ma3SAU06bdQtQ==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", + "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", "cpu": [ "riscv64" ], @@ -8208,9 +7724,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.0.tgz", - "integrity": "sha512-0BnUG9mS8I4SSHr3XsxVhuCMEiu+rX61xxZF5vujso4LaiAGFZFxvDjg6Xn6tLPNTUAfuCvQYas4LMQMVsKRSQ==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", + "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", "cpu": [ "s390x" ], @@ -8225,9 +7741,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.0.tgz", - "integrity": "sha512-Adu/VttB1dpPNW+FEacrZ+xVm9tFty84+RrFzsqlFaPxoJB+9XXyDGtp5dCOoBwGBIEVH0To7lExFXEx0BIF4A==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", + "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", "cpu": [ "x64" ], @@ -8242,9 +7758,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.0.tgz", - "integrity": "sha512-NQ3bDvjUbFKmP23671xUlXtKmqVsUBd6M4PQCvbmNtOy06hnQIdKHy8oG/6S3R/S6He1JgPk6A5VT+prAJMYEw==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", + "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", "cpu": [ "x64" ], @@ -8259,9 +7775,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.0.tgz", - "integrity": "sha512-u2eDAl4+0aFvA13GxlGBtTI3SS3sdgwgtV0HyjZ0QaQVCgNE+jqNGey+GtxWiq+wxr/UycAx/OnfJzApCFamvA==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", + "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", "cpu": [ "x64" ], @@ -8273,9 +7789,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.0.tgz", - "integrity": "sha512-XvRb5vfW3wAZQ+ZUG21AnHHDKtNcw99eigzEhjr//NZ3u7SoBaPP0seSc7FgP7p1epAEdAoZckMW9WY/+4w70w==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", + "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", "cpu": [ "arm64" ], @@ -8287,9 +7803,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.0.tgz", - "integrity": "sha512-iZPmniy4kNBf5yo2RezbkYNNK5HPbXE9+g+twnbqSng7dtLEJy1SKoxiE/ni4FDacjyuZpEeb9U054N4EoKHYw==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", + "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", "cpu": [ "arm64" ], @@ -8301,9 +7817,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.0.tgz", - "integrity": "sha512-mFBBd+LF37fnE8JnYUOH+imj0aPFPK30vpar4ehJkgnLj9sZn8ZxiRENmLtgIwxK7TC8klF6N57fxdNBwQoqOA==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", + "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", "cpu": [ "ia32" ], @@ -8315,9 +7831,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.0.tgz", - "integrity": "sha512-ujeqEY3B+zbGn3Z4Q03cUBG/LGWnBJncVT36WER31LcOsQk9+1dmINKKtvmmfChUvRbK1G0R8OhMWFgHgaZtAw==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", + "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", "cpu": [ "x64" ], @@ -8329,9 +7845,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.0.tgz", - "integrity": "sha512-hncn90N4sOky0L2LKE5oESKLbxCPeVo4eLA2LSMoDzM+879ml4WSr+Rr4DWknNIVVvS1Hirkc9hx02W6YxS8rQ==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", + "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", "cpu": [ "x64" ], @@ -9182,6 +8698,21 @@ "node": ">=8" } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/app-builder-lib": { "version": "26.15.3", "resolved": "https://registry.npmmirror.com/app-builder-lib/-/app-builder-lib-26.15.3.tgz", @@ -9737,39 +9268,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/builder-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/builder-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/builder-util/node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", @@ -9795,19 +9293,6 @@ "dev": true, "license": "MIT" }, - "node_modules/builder-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmmirror.com/bundle-name/-/bundle-name-4.1.0.tgz", @@ -9881,22 +9366,6 @@ "node": ">=8" } }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.9.tgz", @@ -9995,6 +9464,23 @@ "node": ">=18" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/character-entities/-/character-entities-2.0.2.tgz", @@ -10639,6 +10125,10 @@ "resolved": "packages/dsh-desktop-preset-transfer", "link": true }, + "node_modules/dshmarket": { + "resolved": "packages/dshmarket", + "link": true + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -10752,120 +10242,28 @@ "electron-winstaller": "5.4.0" } }, - "node_modules/electron-builder/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/electron-builder/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/electron-builder/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/electron-publish": { - "version": "26.15.3", - "resolved": "https://registry.npmmirror.com/electron-publish/-/electron-publish-26.15.3.tgz", - "integrity": "sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/fs-extra": "^9.0.11", - "aws4": "^1.13.2", - "builder-util": "26.15.3", - "builder-util-runtime": "9.7.0", - "chalk": "^4.1.2", - "form-data": "^4.0.5", - "fs-extra": "^10.1.0", - "lazy-val": "^1.0.5", - "mime": "^2.5.2" - } - }, - "node_modules/electron-publish/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/electron-publish/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/electron-publish/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "26.15.3", + "resolved": "https://registry.npmmirror.com/electron-publish/-/electron-publish-26.15.3.tgz", + "integrity": "sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@types/fs-extra": "^9.0.11", + "aws4": "^1.13.2", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "form-data": "^4.0.5", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" } }, "node_modules/electron-to-chromium": { - "version": "1.5.415", - "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.415.tgz", - "integrity": "sha512-958V+Kbhtgz+SxXeEVKBjrlKRBIDAYvUJfwhjxMZ5S6ut9jAl7l9ZKBkBrvjyjZE36PabLUo2L8kEeV5O4vgJg==", + "version": "1.5.420", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.420.tgz", + "integrity": "sha512-2yD6XreGusOfNV+dUcvipJEXc3n/n7fgr7996aszTG+YY5E4mqM4tOq/3uhP129cazL9YHbVWSpc79ePotWtPA==", "dev": true, "license": "ISC" }, @@ -11291,9 +10689,9 @@ } }, "node_modules/express-rate-limit": { - "version": "8.6.2", - "resolved": "https://registry.npmmirror.com/express-rate-limit/-/express-rate-limit-8.6.2.tgz", - "integrity": "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==", + "version": "8.7.0", + "resolved": "https://registry.npmmirror.com/express-rate-limit/-/express-rate-limit-8.7.0.tgz", + "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==", "license": "MIT", "dependencies": { "debug": "^4.4.3", @@ -11749,6 +11147,75 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/global-agent": { "version": "3.0.0", "resolved": "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz", @@ -12165,9 +11632,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.5.0", - "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.5.0.tgz", - "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==", + "version": "10.7.0", + "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.7.0.tgz", + "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", "license": "MIT", "engines": { "node": ">= 12" @@ -13796,9 +13263,9 @@ } }, "node_modules/node-abi": { - "version": "4.34.0", - "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-4.34.0.tgz", - "integrity": "sha512-4Oy5Q6/Ftna9sXyrkdnKypfvm9uWRpxUPvlw4oA192QNMN39aq8k4l36TUUUU/ONw7ivGVi402Ud+UBPVDYh6A==", + "version": "4.35.0", + "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-4.35.0.tgz", + "integrity": "sha512-ymk4aIzxdPopw2giv8Fs1Ec6vybGkjmyxUwVqhkI4MCy2tVfXdkOGGWieWVjL0THgH+7a8lRdevyupoYj3Js/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -14257,16 +13724,16 @@ } }, "node_modules/open": { - "version": "11.0.1", - "resolved": "https://registry.npmmirror.com/open/-/open-11.0.1.tgz", - "integrity": "sha512-NzwMUB6C1D0+Kd+9iMS/H4k+Ck3cTX6Ckyfr/gAGlmvSE1LUQZnEZvWBi4PYmMwH/S5SMeTXnE+9uAz8uF+pWw==", + "version": "11.0.2", + "resolved": "https://registry.npmmirror.com/open/-/open-11.0.2.tgz", + "integrity": "sha512-RWqF+pBSkqecEvCKOn8QYhaNdRMJDZRIrlS/7rTDdLHaPcfXGCZ/h8zb413NfvdeAV0MR7T1yJcA34/q+CSm1Q==", "license": "MIT", "dependencies": { - "default-browser": "^5.4.0", + "default-browser": "^5.5.1", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", - "powershell-utils": "^0.2.0", + "powershell-utils": "^0.2.1", "wsl-utils": "^1.0.0" }, "engines": { @@ -14414,39 +13881,6 @@ "npm": ">5" } }, - "node_modules/patch-package/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/patch-package/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/patch-package/node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz", @@ -14509,19 +13943,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/patch-package/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", @@ -14740,9 +14161,9 @@ } }, "node_modules/powershell-utils": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/powershell-utils/-/powershell-utils-0.2.0.tgz", - "integrity": "sha512-ZlsFlG7MtSFCoc5xreOvBAozCJ6Pf06opgJjh9ONEv418xpZSAzNjstD36C6+JwOnfSqOW/9uDkqKjezTdxZhw==", + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/powershell-utils/-/powershell-utils-0.2.1.tgz", + "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==", "license": "MIT", "engines": { "node": ">=20" @@ -14824,13 +14245,6 @@ "node": ">= 4" } }, - "node_modules/proper-lockfile/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, "node_modules/property-information": { "version": "7.2.0", "resolved": "https://registry.npmmirror.com/property-information/-/property-information-7.2.0.tgz", @@ -14934,21 +14348,6 @@ "node": ">=10.13.0" } }, - "node_modules/qrcode/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/qrcode/node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz", @@ -15016,9 +14415,9 @@ } }, "node_modules/qs": { - "version": "6.15.3", - "resolved": "https://registry.npmmirror.com/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "version": "6.16.0", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -15297,63 +14696,6 @@ "rimraf": "bin.js" } }, - "node_modules/rimraf/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz", @@ -15374,9 +14716,9 @@ } }, "node_modules/rollup": { - "version": "4.63.0", - "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.63.0.tgz", - "integrity": "sha512-T5vnZ2y4QqC3/4P+w2+JO+Q/OVdnPsv4XcSYJYMEn0R9/jjl5AgLwO9LAZMzP2lN71O6pypn91rB7lDstUkfrQ==", + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.63.1.tgz", + "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", "dev": true, "license": "MIT", "dependencies": { @@ -15391,31 +14733,31 @@ }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.63.0", - "@rollup/rollup-android-arm64": "4.63.0", - "@rollup/rollup-darwin-arm64": "4.63.0", - "@rollup/rollup-darwin-x64": "4.63.0", - "@rollup/rollup-freebsd-arm64": "4.63.0", - "@rollup/rollup-freebsd-x64": "4.63.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.63.0", - "@rollup/rollup-linux-arm-musleabihf": "4.63.0", - "@rollup/rollup-linux-arm64-gnu": "4.63.0", - "@rollup/rollup-linux-arm64-musl": "4.63.0", - "@rollup/rollup-linux-loong64-gnu": "4.63.0", - "@rollup/rollup-linux-loong64-musl": "4.63.0", - "@rollup/rollup-linux-ppc64-gnu": "4.63.0", - "@rollup/rollup-linux-ppc64-musl": "4.63.0", - "@rollup/rollup-linux-riscv64-gnu": "4.63.0", - "@rollup/rollup-linux-riscv64-musl": "4.63.0", - "@rollup/rollup-linux-s390x-gnu": "4.63.0", - "@rollup/rollup-linux-x64-gnu": "4.63.0", - "@rollup/rollup-linux-x64-musl": "4.63.0", - "@rollup/rollup-openbsd-x64": "4.63.0", - "@rollup/rollup-openharmony-arm64": "4.63.0", - "@rollup/rollup-win32-arm64-msvc": "4.63.0", - "@rollup/rollup-win32-ia32-msvc": "4.63.0", - "@rollup/rollup-win32-x64-gnu": "4.63.0", - "@rollup/rollup-win32-x64-msvc": "4.63.0", + "@rollup/rollup-android-arm-eabi": "4.63.1", + "@rollup/rollup-android-arm64": "4.63.1", + "@rollup/rollup-darwin-arm64": "4.63.1", + "@rollup/rollup-darwin-x64": "4.63.1", + "@rollup/rollup-freebsd-arm64": "4.63.1", + "@rollup/rollup-freebsd-x64": "4.63.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", + "@rollup/rollup-linux-arm-musleabihf": "4.63.1", + "@rollup/rollup-linux-arm64-gnu": "4.63.1", + "@rollup/rollup-linux-arm64-musl": "4.63.1", + "@rollup/rollup-linux-loong64-gnu": "4.63.1", + "@rollup/rollup-linux-loong64-musl": "4.63.1", + "@rollup/rollup-linux-ppc64-gnu": "4.63.1", + "@rollup/rollup-linux-ppc64-musl": "4.63.1", + "@rollup/rollup-linux-riscv64-gnu": "4.63.1", + "@rollup/rollup-linux-riscv64-musl": "4.63.1", + "@rollup/rollup-linux-s390x-gnu": "4.63.1", + "@rollup/rollup-linux-x64-gnu": "4.63.1", + "@rollup/rollup-linux-x64-musl": "4.63.1", + "@rollup/rollup-openbsd-x64": "4.63.1", + "@rollup/rollup-openharmony-arm64": "4.63.1", + "@rollup/rollup-win32-arm64-msvc": "4.63.1", + "@rollup/rollup-win32-ia32-msvc": "4.63.1", + "@rollup/rollup-win32-x64-gnu": "4.63.1", + "@rollup/rollup-win32-x64-msvc": "4.63.1", "fsevents": "~2.3.2" } }, @@ -15827,6 +15169,13 @@ "dev": true, "license": "ISC" }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -16027,6 +15376,19 @@ "dev": true, "license": "MIT" }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tar": { "version": "7.5.22", "resolved": "https://registry.npmmirror.com/tar/-/tar-7.5.22.tgz", @@ -16303,9 +15665,9 @@ } }, "node_modules/undici": { - "version": "8.10.0", - "resolved": "https://registry.npmmirror.com/undici/-/undici-8.10.0.tgz", - "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==", + "version": "8.10.1", + "resolved": "https://registry.npmmirror.com/undici/-/undici-8.10.1.tgz", + "integrity": "sha512-YQ3WlbqjYMmNpdvDH64jAgLjxuAR9+649calDWhbshYaeQGO2bR4nI94ORJmwI3J9YhoKQnpyGOK+0zlWS5N5Q==", "license": "MIT", "engines": { "node": ">=22.19.0" @@ -17265,22 +16627,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", @@ -17421,9 +16767,9 @@ } }, "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmmirror.com/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -17481,9 +16827,9 @@ "license": "MIT", "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.1" + "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.4" } }, "packages/dsh-desktop-hmr-fallback": { @@ -17501,7 +16847,7 @@ }, "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1" + "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.4" } }, "packages/dsh-desktop-preset-transfer": { @@ -17509,9 +16855,39 @@ "license": "MIT", "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.1", + "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.4", "fflate": "^0.8.2" } + }, + "packages/dshmarket": { + "version": "1.40.0", + "license": "MIT", + "dependencies": { + "js-yaml": "^4.1.0", + "undici": "^7.29.0" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "^4.0.1", + "@deepseek-ai/dsh-settings": "^0.1.0-rc.7 || ^0.1.1-rc.2 || ^0.1.2-alpha.2", + "@deepseek-ai/schemastery": "^3.18.1" + }, + "peerDependenciesMeta": { + "@deepseek-ai/dsh-settings": { + "optional": true + }, + "@deepseek-ai/schemastery": { + "optional": true + } + } + }, + "packages/dshmarket/node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } } } } diff --git a/package.json b/package.json index 0ea5aca5e..54420ddd3 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "postinstall": "patch-package && node scripts/install-brand-assets.mjs && install-electron --no", "dev": "electron-vite dev", "build": "electron-vite build", + "build:market": "npm --prefix packages/dshmarket run build", "icons:generate": "node scripts/generate-app-icons.mjs", "preview": "electron-vite preview", "typecheck": "tsc --noEmit -p tsconfig.node.json", @@ -42,233 +43,237 @@ "package:win": "node scripts/verify-target.mjs win32 x64 && npm run build && electron-builder --win --x64 --publish never" }, "dependencies": { - "@deepseek-ai/cordis": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-4.0.1.tgz", - "@deepseek-ai/cordis-plugin-group": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-group-1.0.1.tgz", - "@deepseek-ai/cordis-plugin-hmr": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-hmr-1.0.16.tgz", - "@deepseek-ai/cordis-plugin-include": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-include-1.0.6.tgz", - "@deepseek-ai/cordis-plugin-loader": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-loader-1.0.2.tgz", - "@deepseek-ai/cordis-plugin-timer": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cordis-plugin-timer-1.1.3.tgz", - "@deepseek-ai/cosmokit": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-cosmokit-1.8.2.tgz", - "@deepseek-ai/dsh": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-acp": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-acp-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-acp-app": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-acp-app-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-default-model": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-default-model-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-instructions": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-instructions-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-loop": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-loop-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-presets": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-presets-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-spine-demo": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-spine-demo-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-agent-tool-presentation": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-agent-tool-presentation-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-anonymous-user-id": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-anonymous-user-id-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-api-gateway": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-gateway-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-api-remotes": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-remotes-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-api-session-controller": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-session-controller-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-api-settings-controller": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-settings-controller-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-api-workspace-controller": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-api-workspace-controller-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-app-boot": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-app-boot-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-atomic-write": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-atomic-write-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-attachment": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-attachment-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-attachment-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-attachment-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-authorization": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-authorization-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-base": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-base-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-bash-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-bash-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-bash-sandbox": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-bash-sandbox-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-brand": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-brand-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-connection": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-connection-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-hmr": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-hmr-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-locale": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-locale-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-modules": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-modules-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-store": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-store-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-agent-preset": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-agent-preset-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-approval": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-approval-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-attachment": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-attachment-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-brand-official": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-brand-official-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-chat": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-chat-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-commands": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-commands-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-conversation": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-conversation-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-cordis": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-cordis-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-deliverables": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-deliverables-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-directory-picker-browse": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-browse-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-directory-picker-native": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-native-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-goal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-goal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-input-trigger": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-input-trigger-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-jobs": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-jobs-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-layout": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-layout-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-message-feedback": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-message-feedback-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-model-selection": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-model-selection-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-permission-presets": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-permission-presets-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-plan": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-plan-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-primitives": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-primitives-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-reference": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-reference-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-renderer": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-renderer-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-session": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-session-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-settings": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-settings-general": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-general-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-settings-models": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-models-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-settings-plugin-inventory": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugin-inventory-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-settings-plugins": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugins-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-sidebar": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-sidebar-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-skill": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-skill-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-slots": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-slots-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-subagent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-subagent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-theme": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-theme-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-tool": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-tool-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-trajectory": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-trajectory-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-user-questions": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-user-questions-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-workflow-run": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-workflow-run-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-client-ui-workspace": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-client-ui-workspace-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-cmdline": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-cmdline-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-code-runtime": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-code-runtime-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-code-runtime-worker-thread": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-code-runtime-worker-thread-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-command-compact": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-command-compact-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-command-feedback": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-command-feedback-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-command-goal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-command-goal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-commands": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-commands-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-compaction": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-compaction-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-compaction-basic": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-compaction-basic-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-compaction-tool-result-pruner": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-compaction-tool-result-pruner-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-cordis-client-runner": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-cordis-client-runner-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-cordis-host-runner": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-cordis-host-runner-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-credentials": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-credentials-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-credentials-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-credentials-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-deepseek-llm-api-extensions": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-deepseek-llm-api-extensions-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-file-reference": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-file-reference-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-file-reference-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-file-reference-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-fs": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-fs-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-fs-observation-policy": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-observation-policy-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-fs-sandbox": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-fs-sandbox-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-goal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-goal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-goal-round-driver": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-goal-round-driver-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-headless": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-headless-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-home-paths": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-home-paths-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-hook-protocol": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-hook-protocol-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-hooks-claude-code": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-hooks-claude-code-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-hooks-codex": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-hooks-codex-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-directory-picker": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-directory-picker-auto": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-auto-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-directory-picker-browse": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-browse-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-directory-picker-native": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-directory-picker-native-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-frontend-static": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-frontend-static-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-plugin-inventory": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-plugin-inventory-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-host-webserver": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-host-webserver-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-invariants": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-invariants-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-jobs": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-jobs-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-jobs-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-jobs-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-launch-environment": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-launch-environment-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-llm": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-llm-deepseek": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-deepseek-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-llm-pi-ai": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-pi-ai-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-llm-retry": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-llm-retry-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-mcp-client": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-mcp-client-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-message-feedback": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-message-feedback-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-native-command": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-native-command-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-output-retention": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-output-retention-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-permission-presets": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-permission-presets-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-persona": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-persona-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-plan-mode": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-plan-mode-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-plugin-package-inventory-deepseek": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-plugin-package-inventory-deepseek-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-pwsh-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-pwsh-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-pwsh-sandbox": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-pwsh-sandbox-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-repeat-tool-reminder": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-repeat-tool-reminder-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sandbox": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sandbox-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sandbox-policy": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-policy-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sandbox-windows-acl": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sandbox-windows-acl-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-schedule": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-schedule-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-scope": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-scope-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sdk-app": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-app-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sdk-jsonrpc-server": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-jsonrpc-server-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sdk-minimal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-minimal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-sdk-protocol": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-sdk-protocol-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-checkpoint-policy": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-checkpoint-policy-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-log-deepseek": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-log-deepseek-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-log-export": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-log-export-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-persistence": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-persistence-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-persistence-jsonl": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-persistence-jsonl-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-projection": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-projection-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-projection-cache": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-projection-cache-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-query": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-query-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-query-sqlite": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-query-sqlite-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-reference": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-reference-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-stats": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-stats-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-telemetry": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-telemetry-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-telemetry-otel": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-telemetry-otel-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-title": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-title-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-title-first-prompt-llm": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-title-first-prompt-llm-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-session-title-llm": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-session-title-llm-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-settings": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-settings-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-settings-file": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-settings-file-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-shell": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-shell-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-shell-env": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-shell-env-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-skill": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-skill-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-skill-badge": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-skill-badge-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-skill-filesystem": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-skill-filesystem-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-spill": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-spill-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-spill-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-spill-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-spill-policy": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-spill-policy-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-storage": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-storage-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-storage-domain": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-storage-domain-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-storage-json": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-storage-json-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subagent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subagent-fork-in-process": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-fork-in-process-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subagent-in-process-driver": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-in-process-driver-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subagent-spawn-in-process": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subagent-spawn-in-process-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subprocess": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subprocess-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-subprocess-local": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-subprocess-local-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-system-prompt": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-system-prompt-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-terminal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-terminal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-terminal-bash": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-terminal-bash-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-time-context": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-time-context-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-timeout": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-timeout-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tmux-context": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tmux-context-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-token-meter": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-token-meter-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-ask-user": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-ask-user-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-bash": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-bash-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-bash-persistent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-bash-persistent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-call-timeout-policy": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-call-timeout-policy-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-cordis": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-cordis-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-fs": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-fs-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-fs-search": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-fs-search-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-goal": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-goal-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-jobs": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-jobs-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-pwsh": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-pwsh-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-pwsh-persistent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-pwsh-persistent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-ralph": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-ralph-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-skill": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-skill-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-str-replace-editor": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-str-replace-editor-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-subagent": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-subagent-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-subagent-control": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-subagent-control-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-subagent-report": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-subagent-report-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-todo": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-todo-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-web": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-web-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tool-workflow": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tool-workflow-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-tools": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-tools-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-typert-generator": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-generator-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-typert-loader": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-loader-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-typert-protocol": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-protocol-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-typert-registry": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-typert-registry-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-user-approval": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-user-approval-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-user-questions": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-user-questions-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-util-crypto": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-util-crypto-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-util-workspace-path": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-util-workspace-path-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-web": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-web-app": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-app-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-web-fetch-http": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-fetch-http-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-web-frontend": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-frontend-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-web-search-deepseek": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-web-search-deepseek-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-webhook": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-webhook-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-webhook-github": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-webhook-github-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-win32-process": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-win32-process-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-workflow": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-workflow-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-workflow-worker-thread": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-workflow-worker-thread-0.1.2-alpha.1.tgz", - "@deepseek-ai/dsh-workspace": "file:packages/harness-0.1.2-alpha.1/npm-dsh/deepseek-ai-dsh-workspace-0.1.2-alpha.1.tgz", - "@deepseek-ai/schemastery": "file:packages/harness-0.1.2-alpha.1/npm-vendor/deepseek-ai-schemastery-3.18.1.tgz", + "@deepseek-ai/cordis": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-4.0.2.tgz", + "@deepseek-ai/cordis-plugin-group": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-group-1.0.2.tgz", + "@deepseek-ai/cordis-plugin-hmr": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-hmr-1.0.17.tgz", + "@deepseek-ai/cordis-plugin-include": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-include-1.0.7.tgz", + "@deepseek-ai/cordis-plugin-loader": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-loader-1.0.3.tgz", + "@deepseek-ai/cordis-plugin-timer": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cordis-plugin-timer-1.1.4.tgz", + "@deepseek-ai/cosmokit": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-cosmokit-1.8.3.tgz", + "@deepseek-ai/dsh": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-acp": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-acp-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-acp-app": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-acp-app-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent-default-model": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-default-model-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent-instructions": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-instructions-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent-loop": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-loop-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent-presets": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-presets-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-agent-tool-presentation": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-agent-tool-presentation-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-anonymous-user-id": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-anonymous-user-id-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-api-gateway": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-gateway-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-api-remotes": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-remotes-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-api-session-controller": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-session-controller-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-api-settings-controller": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-settings-controller-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-api-workspace-controller": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-api-workspace-controller-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-app-boot": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-app-boot-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-atomic-write": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-atomic-write-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-attachment": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-attachment-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-attachment-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-attachment-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-authorization": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-authorization-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-base": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-base-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-bash-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-bash-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-bash-sandbox": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-bash-sandbox-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-brand": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-brand-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-connection": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-connection-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-hmr": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-hmr-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-locale": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-locale-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-modules": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-modules-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-store": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-store-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-agent-preset": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-agent-preset-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-approval": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-approval-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-attachment": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-attachment-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-brand-official": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-brand-official-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-chat": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-chat-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-commands": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-commands-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-conversation": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-conversation-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-cordis": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-cordis-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-deliverables": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-deliverables-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-directory-picker-browse": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-browse-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-directory-picker-native": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-directory-picker-native-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-goal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-goal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-input-trigger": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-input-trigger-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-jobs": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-jobs-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-layout": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-layout-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-message-feedback": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-message-feedback-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-model-selection": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-model-selection-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-permission-presets": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-permission-presets-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-plan": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-plan-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-primitives": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-primitives-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-reference": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-reference-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-renderer": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-renderer-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-schedule": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-schedule-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-session": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-session-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-settings": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-settings-general": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-general-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-settings-models": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-models-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-settings-plugin-inventory": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugin-inventory-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-settings-plugins": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-settings-plugins-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-sidebar": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-sidebar-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-skill": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-skill-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-slots": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-slots-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-subagent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-subagent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-theme": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-theme-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-tool": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-tool-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-trajectory": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-trajectory-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-user-questions": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-user-questions-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-workflow-run": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-workflow-run-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-client-ui-workspace": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-client-ui-workspace-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-cmdline": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-cmdline-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-code-runtime": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-code-runtime-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-code-runtime-worker-thread": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-code-runtime-worker-thread-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-command-compact": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-command-compact-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-command-feedback": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-command-feedback-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-command-goal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-command-goal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-commands": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-commands-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-compaction": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-compaction-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-compaction-basic": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-compaction-basic-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-compaction-tool-result-pruner": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-compaction-tool-result-pruner-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-cordis-client-runner": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-cordis-client-runner-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-cordis-host-runner": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-cordis-host-runner-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-credentials": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-credentials-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-credentials-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-credentials-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-deepseek-llm-api-extensions": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-deepseek-llm-api-extensions-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-deque": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-deque-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-file-reference": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-file-reference-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-file-reference-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-file-reference-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-fs": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-fs-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-fs-observation-policy": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-observation-policy-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-fs-sandbox": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-fs-sandbox-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-goal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-goal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-goal-round-driver": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-goal-round-driver-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-headless": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-headless-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-home-paths": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-home-paths-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-hook-protocol": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-hook-protocol-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-hooks-claude-code": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-hooks-claude-code-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-hooks-codex": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-hooks-codex-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-directory-picker": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-directory-picker-auto": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-auto-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-directory-picker-browse": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-browse-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-directory-picker-native": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-directory-picker-native-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-frontend-static": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-frontend-static-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-plugin-inventory": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-plugin-inventory-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-host-webserver": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-host-webserver-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-invariants": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-invariants-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-jobs": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-jobs-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-jobs-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-jobs-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-launch-environment": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-launch-environment-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-llm": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-llm-deepseek": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-deepseek-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-llm-pi-ai": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-pi-ai-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-llm-retry": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-llm-retry-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-mcp-client": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-mcp-client-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-message-feedback": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-message-feedback-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-native-command": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-native-command-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-output-retention": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-output-retention-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-permission-presets": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-permission-presets-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-persona": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-persona-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-plan-mode": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-plan-mode-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-plugin-package-inventory-deepseek": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-plugin-package-inventory-deepseek-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-pwsh-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-pwsh-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-pwsh-sandbox": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-pwsh-sandbox-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-repeat-tool-reminder": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-repeat-tool-reminder-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sandbox": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sandbox-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sandbox-policy": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-policy-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sandbox-windows-acl": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sandbox-windows-acl-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-schedule": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-schedule-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-scope": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-scope-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sdk-app": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-app-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sdk-jsonrpc-server": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-jsonrpc-server-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sdk-minimal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-minimal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-sdk-protocol": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-sdk-protocol-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-checkpoint-policy": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-checkpoint-policy-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-log-deepseek": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-log-deepseek-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-log-export": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-log-export-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-persistence": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-persistence-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-persistence-jsonl": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-persistence-jsonl-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-projection": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-projection-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-projection-cache": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-projection-cache-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-query": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-query-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-query-sqlite": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-query-sqlite-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-reference": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-reference-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-stats": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-stats-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-telemetry": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-telemetry-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-telemetry-otel": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-telemetry-otel-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-title": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-title-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-title-first-prompt-llm": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-title-first-prompt-llm-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-title-llm": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-title-llm-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-session-turn-outline": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-session-turn-outline-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-settings": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-settings-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-settings-file": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-settings-file-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-shell": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-shell-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-shell-env": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-shell-env-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-skill": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-skill-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-skill-badge": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-skill-badge-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-skill-filesystem": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-skill-filesystem-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-spill": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-spill-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-spill-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-spill-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-spill-policy": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-spill-policy-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-storage": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-storage-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-storage-domain": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-storage-domain-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-storage-json": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-storage-json-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subagent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subagent-fork-in-process": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-fork-in-process-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subagent-in-process-driver": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-in-process-driver-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subagent-spawn-in-process": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subagent-spawn-in-process-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subprocess": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subprocess-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-subprocess-local": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-subprocess-local-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-system-prompt": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-system-prompt-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-terminal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-terminal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-terminal-bash": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-terminal-bash-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-time-context": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-time-context-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-timeout": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-timeout-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tmux-context": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tmux-context-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-token-meter": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-token-meter-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-ask-user": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-ask-user-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-bash": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-bash-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-bash-persistent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-bash-persistent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-call-timeout-policy": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-call-timeout-policy-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-cordis": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-cordis-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-fs": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-fs-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-fs-search": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-fs-search-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-goal": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-goal-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-jobs": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-jobs-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-pwsh": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-pwsh-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-pwsh-persistent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-pwsh-persistent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-ralph": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-ralph-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-skill": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-skill-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-str-replace-editor": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-str-replace-editor-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-subagent": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-subagent-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-subagent-control": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-subagent-control-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-todo": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-todo-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-web": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-web-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tool-workflow": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tool-workflow-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-tools": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-tools-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-typert-generator": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-generator-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-typert-loader": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-loader-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-typert-protocol": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-protocol-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-typert-registry": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-typert-registry-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-user-approval": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-user-approval-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-user-questions": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-user-questions-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-util-crypto": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-crypto-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-util-time": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-time-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-util-values": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-values-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-util-workspace-path": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-util-workspace-path-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-web": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-web-app": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-app-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-web-fetch-http": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-fetch-http-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-web-frontend": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-frontend-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-web-search-deepseek": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-web-search-deepseek-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-webhook": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-webhook-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-webhook-github": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-webhook-github-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-win32-process": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-win32-process-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-workflow": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-workflow-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-workflow-worker-thread": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-workflow-worker-thread-0.1.2-alpha.4.tgz", + "@deepseek-ai/dsh-workspace": "file:packages/harness-0.1.2-alpha.4/npm-dsh/deepseek-ai-dsh-workspace-0.1.2-alpha.4.tgz", + "@deepseek-ai/schemastery": "file:packages/harness-0.1.2-alpha.4/npm-vendor/deepseek-ai-schemastery-3.18.2.tgz", "dsh-desktop-client-ui": "file:packages/dsh-desktop-client-ui", "dsh-desktop-hmr-fallback": "file:packages/dsh-desktop-hmr-fallback", "dsh-desktop-market-installer": "file:packages/dsh-desktop-market-installer", "dsh-desktop-preset-transfer": "file:packages/dsh-desktop-preset-transfer", + "dshmarket": "file:packages/dshmarket", "electron-updater": "^6.8.9", "node": "24.9.0", "pnpm": "10.34.5", diff --git a/packages/dsh-desktop-client-ui/package.json b/packages/dsh-desktop-client-ui/package.json index 1e51513d3..cf4d4e7d7 100644 --- a/packages/dsh-desktop-client-ui/package.json +++ b/packages/dsh-desktop-client-ui/package.json @@ -23,8 +23,8 @@ "license": "MIT", "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1", - "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.1" + "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.4", + "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.4" } } diff --git a/packages/dsh-desktop-market-installer/index.js b/packages/dsh-desktop-market-installer/index.js index c03a65b02..34539852a 100644 --- a/packages/dsh-desktop-market-installer/index.js +++ b/packages/dsh-desktop-market-installer/index.js @@ -20,7 +20,7 @@ import { import { SIDELINE_MARKER } from './pnpm-runner.mjs' import { removeTree } from './remove-tree.mjs' -export const RECOMMENDED_MARKET_VERSION = 'latest' +export const RECOMMENDED_MARKET_VERSION = '^1.40.0' export const MARKET_PACKAGE = 'dshmarket' export const MARKET_PROFILE = 'web' export const STATUS_PATH = '/dsh-desktop/market-installer/status' diff --git a/packages/dsh-desktop-market-installer/package.json b/packages/dsh-desktop-market-installer/package.json index 3a4988f73..a2284f8de 100644 --- a/packages/dsh-desktop-market-installer/package.json +++ b/packages/dsh-desktop-market-installer/package.json @@ -35,7 +35,7 @@ "license": "MIT", "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.1" + "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.4" }, "dependencies": { "pnpm": "10.34.5" diff --git a/packages/dsh-desktop-preset-transfer/index.js b/packages/dsh-desktop-preset-transfer/index.js index a81865a5f..a37e22bdc 100644 --- a/packages/dsh-desktop-preset-transfer/index.js +++ b/packages/dsh-desktop-preset-transfer/index.js @@ -28,7 +28,7 @@ const EXPORT_PATH = '/api/agent-preset.export' const IMPORT_PATH = '/api/agent-preset.import' /** Harness version stamped into an exported manifest. */ -const PRESET_SOURCE_DSH_VERSION = '0.1.2-alpha.1' +const PRESET_SOURCE_DSH_VERSION = '0.1.2-alpha.4' const PRESET_ARCHIVE_FORMAT = "dsh-preset"; const PRESET_ARCHIVE_VERSION = 1; diff --git a/packages/dsh-desktop-preset-transfer/package.json b/packages/dsh-desktop-preset-transfer/package.json index b34a7be88..8cc8fa6dd 100644 --- a/packages/dsh-desktop-preset-transfer/package.json +++ b/packages/dsh-desktop-preset-transfer/package.json @@ -15,7 +15,7 @@ "license": "MIT", "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1", - "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.1", + "@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.4", "fflate": "^0.8.2" }, "types": "./index.d.ts" diff --git a/packages/dshmarket/.gitattributes b/packages/dshmarket/.gitattributes new file mode 100644 index 000000000..8988c4844 --- /dev/null +++ b/packages/dshmarket/.gitattributes @@ -0,0 +1,13 @@ +# Source files check out with LF everywhere. Without this, a Windows +# checkout under autocrlf gets CRLF sources, the client sourcemap embeds +# them in sourcesContent, and the committed map can never match a Windows +# rebuild — the reproducibility guard measured exactly that on its second +# run (#472). The bundle itself was already line-ending-clean; the map is +# where checkout-time conversion leaks into a build artifact. +* text=auto eol=lf +# Binary-ish assets keep their bytes. +*.png -text +*.jpg -text +*.gif -text +*.ico -text +*.woff2 -text diff --git a/packages/dshmarket/.gitignore b/packages/dshmarket/.gitignore new file mode 100644 index 000000000..fd128193a --- /dev/null +++ b/packages/dshmarket/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +lib/ +research/ +*.tgz +.DS_Store +npm-cache/ +docs/ +data/readmes-snapshot.json diff --git a/packages/dshmarket/IMPROVEMENT-PLAN.md b/packages/dshmarket/IMPROVEMENT-PLAN.md new file mode 100644 index 000000000..51ca46f73 --- /dev/null +++ b/packages/dshmarket/IMPROVEMENT-PLAN.md @@ -0,0 +1,342 @@ +# dshmarket(DSH 插件市场)可落地改进方案 + +> 版本:v1.0 | 对象:dshmarket 插件(仓库 `dsh-market`,npm 名 `dshmarket`) +> 依据:针对 `src/routes.ts`(编译产物 `lib/routes.js`)、`client/client.js`、`src/registry.ts` + `data/registry-snapshot.json`、`scripts/preflight.mjs`、宿主 `dsh plugin` CLI 的审计。 + +--- + +## 0. 审计基线(现状盘点,防止"重复造轮子") + +先说明现状里**已经有**的能力,本方案只补缺口: + +| 能力 | 现状位置 | 现状 | 缺口 | +|---|---|---|---| +| 安装执行 | `src/routes.ts` `/dsh-market/install` | 走 `dsh plugin add`,目标 = `entry.npm` 或 `github:repo` | 无 monorepo 子包/subpath 支持;无目标存在性与 `dsh.bundle` 前置校验 | +| 装后生效 | `src/hot.ts` hotMount + `/install` 返回 `hot` 布尔 | 纯 insert patch 可热挂 | 不校验是否真正进入 `dsh.profile.bundles`;失败原因不区分,"重启后生效"一刀切 | +| 更新 | `/dsh-market/update` re-add `name@latest` / `github:repo` | 只看 exitCode | 无 before/after 版本对比;`minimumReleaseAge` 导致的"假更新"无感知 | +| 进度 | `/dsh-market/status` 轮询 lastLine + `parseProgressPct`(client.js) | 已有进度条 + 取消按钮 | 依赖 pnpm 非 TTY 聚合行,CI 下常不吐 → 长时间"安装中…",无阶段/包名 | +| 错误呈现 | client.js `installError` 单行 + 页头"导出日志"链接 | 有日志导出(`/dsh-market/logs`,脱敏) | stderr 尾部 600 字符一坨,无错误码/关键行解析,无错误旁的导出按钮 | +| 构建脚本 | 无(宿主 CLI 只在 git 安装失败时打印一行提示,见 `dsh` 的 `lib/plugin-*.js`:提示把 pnpm 打印的 key 写入 `/pnpm-workspace.yaml` 的 `allowBuilds`) | — | UI 无"批准构建脚本并重试"入口 | +| 目录数据 | `data/registry-snapshot.json`(237 条)+ `src/registry.ts`(TTL 1h + 快照回退) | 字段:name/owner/url/category/description/npm/stars/install/added | 无 subpath/package 映射;npm 名只做格式校验不做存在性校验;无目录过期提示与手动刷新 | + +**关键宿主事实**(方案依赖这些真实机制): +- profile 目录 = `$DSH_HOME/profiles/`,内含 `package.json`(依赖 + manifest `dsh.profile.bundles`)与 `pnpm-workspace.yaml`(可写构建放行配置)。 +- `dsh plugin ` 是 pnpm 薄转发器:在 profile 目录跑 `pnpm `,成功后按"是否声明 `dsh.bundle`"把依赖 reconcile 进 `dsh.profile.bundles`;无 `dsh.bundle` 的依赖只警告、不进 bundles(装成普通依赖)。 +- 因此"装完是否生效"的真值就是:**包名是否出现在 `/package.json` 的 `dsh.profile.bundles` 里**。 +- `--reporter=ndjson` 是 pnpm 原生选项,可拿到结构化进度事件(`progress` / `fetchingProgress`),pnpm 官方 issue 有跟踪记录。 + +**改动位置约定**:源码在 `src/*.ts`,构建产物为 `lib/*.js`(下文写"src/routes.ts(→ lib/routes.js)"指同一处);`client/client.js` 是**无构建步骤**的手写 CJS bundle(`scripts/preflight.mjs` 校验其 loader id),新组件一律以内联子组件实现,不引入新构建链。 + +--- + +## 1. 优先级总览 + +| 编号 | 改进项 | 优先级 | 改动位置 | 一句话价值 | +|---|---|---|---|---| +| P0-1 | 安装目标解析:monorepo 感知 + 安装前校验 | **P0** | registry schema + `src/routes.ts` + client | 消灭"装错目标/装了个不生效的东西" | +| P0-2 | 安装后验证与"已安装但未生效(原因)" | **P0** | 新 `src/verify.ts` + 三处路由 + client | 装完立即知道生效没有、为什么 | +| P0-3 | 更新版本校验(含 minimumReleaseAge 说明与一键处理) | **P0** | `/update` 路由 + client | 消灭"假更新"事故 | +| P0-4 | 构建脚本放行 UI(allowBuilds + 一键重试) | **P0** | 新路由 + 新对话框组件 | 最常见的首装失败变成一键解决 | +| P1-5 | 错误呈现:stderr 关键行 + 错误旁导出日志 | **P1** | `src/diagnose.ts` + `ErrorPanel` 组件 | 报错可看懂、可复制、可提交 | +| P1-6 | 进度/取消:真实阶段进度(ndjson) | **P1** | `runDshPlugin` + `/status` + 进度条 | 安装过程透明、可安全取消 | +| P1-7 | 安装目标锁定 commit(可复现安装) | **P1** | `resolveTarget` + `/install` | 可复现、更新有明确的 commit diff | +| P1-8 | 目录数据治理(schema 校验、过期提示、刷新) | **P1** | `scripts/preflight.mjs` + 新校验脚本 + client | 目录可信,坏条目不上架 | +| P1-9 | 结构化诊断与操作建议(错误分类服务) | **P1** | 新 `src/diagnose.ts` | 每类错误给一个可执行动作 | +| P2-10 | 目标 profile 选择 | **P2** | 新路由 + 下拉组件 | 终端类插件装对地方 | +| P2-11 | 安装台账(journal) | **P2** | `src/log.ts` 扩展 + 新路由 | 可审计、可回溯 | +| P2-12 | 断线恢复与幂等增强 | **P2** | 路由 + client | 刷新/断网不丢状态 | +| P2-13 | 打磨:无障碍、i18n、性能 | **P2** | client | 细节体验 | + +--- + +# P0 —— 必须修(直接造成错误结果或死胡同) + +## P0-1 安装目标解析:monorepo 感知 + 安装前校验 + +**事故映射**:目录中相当一部分条目是"仓库根不可装"的真实形态——文档/聚合仓库根无 `package.json`,真包在 `packages/xxx` 子目录;或子包已发布到 npm 但目录没填 `npm` 字段。当前 `/install` 只做 `entry.npm` 或 `github:repo` 二选一(`src/routes.ts`),monorepo 仓库会装错目标或装失败;`dsh plugin` 对无 `dsh.bundle` 的依赖只打一行警告就把它写进 dependencies("已安装"但没有生效,还污染 profile)。没有任何"目标包存在且声明 `dsh.bundle`"的前置校验,`NPM_NAME_RE` 只校验名字格式。 + +**改动位置**: +- 目录数据 `data/registry-snapshot.json` + `src/registry.ts` 的 `RegistryPlugin` 接口(schema 扩展); +- `src/routes.ts`(→ `lib/routes.js`)`/dsh-market/install` 与新增 `resolveTarget(entry)` / `preflightTarget(target)`; +- `client/client.js`:确认弹窗展示解析后的目标,卡片加"monorepo 子包"徽标,`isInstalled` 扩展匹配; +- `scripts/preflight.mjs`:registry 条目合法性校验(发布前 gate)。 + +**具体做法**: +1. **registry schema 扩展**(可选字段,向后兼容): + - `subpath?: string` — 仓库内包目录(如 `packages/dsh-foo`),语义 = "仓库根不可装,真包在此子目录"; + - `package?: string` — 该子包在 npm 上的发布名(若已发布,优先走 npm); + - `npm?: string` — 保留,整包 npm 名,最高优先级; + - `uninstallable?: boolean` — 显式标记"该仓库无 DSH 插件可装"(纯文档站)。 +2. **目标解析顺序** `resolveTarget(entry)`: + a. `entry.npm` 合法 → `npm:`(registry tarball,最小下载量,优先); + b. 否则 `entry.package` 合法 → `npm:`; + c. 否则 `entry.subpath` 存在 → git 子目录语法 `github:owner/repo#` + 子目录(pnpm ≥9 支持 git URL 子目录,见 [pnpm PR #7487](https://github.com/pnpm/pnpm/pull/7487) 与 [讨论 #8148](https://github.com/orgs/pnpm/discussions/8148);实施时以本地 pnpm 版本实测语法并做版本探测,语法不兼容则**报可操作错误**而不是静默装根包); + d. 否则(仓库根可装)→ `github:owner/repo#`。 +3. **安装前校验** `preflightTarget`(4s 超时 + 内存缓存,失败降级不阻塞): + - npm 目标:`pnpm view version dsh.bundle`(或 npm registry metadata `versions[latest].dsh`)→ 包不存在 → **拦截**,报"目标包不存在/目录 npm 字段过期";存在但无 `dsh.bundle` → 黄色告警"该包未声明 dsh.bundle,装入后不会成为 profile 层(可能仅客户端插件)",允许继续但明确后果; + - git 目标:raw.githubusercontent.com 拉 `/package.json` 校验存在 + `dsh.bundle`; + - 校验结果随 install 响应返回 `preflight: { exists, bundle, reason }`,确认弹窗就展示。 +4. **客户端**:确认弹窗显示"将安装:`@scope/dsh-foo`(来源:npm 包)"或"来源:GitHub 子目录 packages/dsh-foo";`isInstalled` 增加按 `package`/`subpath` 解析出的包名匹配;`uninstallable` 条目置灰并显示"该仓库不包含可安装的 DSH 插件"。 + +**验收标准**: +- 构造 3 条测试目录数据(monorepo 子目录、子包已发布 npm、纯文档仓库):前两者装到正确的子包并进入 bundles;第三者前端置灰不可装。 +- 安装不存在的 npm 名或未声明 `dsh.bundle` 的包:安装前即拦截/告警,不进入 1–3 分钟的 pnpm 流程。 +- `isInstalled` 能识别 subpath 条目对应的已安装包;确认弹窗展示解析后的真实目标。 + +**用户收益**:不再出现"点了安装、等半天、装了个不对的东西/什么都没装成";装之前就知道装的是什么、装进去会不会生效。 + +--- + +## P0-2 安装后验证与"已安装但未生效(原因)"提示 + +**事故映射**:当前 install 返回 `ok + hot` 布尔,hotMount 只在 patch 是纯 `id/name` insert 时成功(`src/hot.ts` 的 `parseSimplePatch`),失败统一落进"重启后生效"横幅。但用户无法区分四种结局:patch 含 config/disable 行(热挂不支持)、CLI 认为无 `dsh.bundle`(装成了普通依赖)、纯客户端插件(只有 `dsh.client` 没有 bundle patch)、真装坏了。对 web profile,"重启后生效"= 整个 dsh 重启,代价大,用户有权知道为什么不能热加载。 + +**改动位置**: +- 新 `src/verify.ts`:`verifyActivation(profile, name)`; +- `src/routes.ts`:install/uninstall/update 三处响应统一带 `activation`;hotMount 失败路径返回具体原因; +- `client/client.js`:结果呈现改三态 + 原因展开。 + +**具体做法**: +1. `verifyActivation(profile, name)` 输出 `{ state: 'live' | 'restart' | 'inert' | 'broken', reasons: string[] }`: + - 读 `/package.json` 的 `dsh.profile.bundles`(CLI reconcile 后的真值)→ 是否含 name; + - 读 `node_modules//package.json` 的 `dsh.bundle` / `dsh.client` / `main`; + - 读 `cordis.patch.yml` 并复用 `parseSimplePatch` 判定可否热挂; + - 汇总原因(示例:"patch 含配置行,热挂载仅支持纯 insert"、"未声明 dsh.bundle,已作为普通依赖安装"、"仅客户端插件,无 bundle patch")。 +2. 三处操作响应带 `activation`;hotMount 的 false 路径把"为什么不行"(Include 不可用 / patch 复杂 / 无 patch)传出来而不是只返回布尔。 +3. 客户端四态呈现(替换现在的"重启后生效"一刀切): + - 🟢 **已生效(热加载)**; + - 🟠 **已安装,重启后生效** —— 原因:xxx; + - ⚪ **已安装但未成为 profile 层** —— 原因:该包未声明 dsh.bundle(可能仅客户端插件),附 README 链接; + - 🔴 **安装完成但校验失败** —— 附"导出日志"。 +4. `inert` 状态引导:链接包 README / 提示适合的 profile(与现有 terminal 警告合并为统一的"目标适配"提示)。 + +**验收标准**: +- 用三个测试包(纯 insert patch / 含 config 的 patch / 无 dsh.bundle 的库)安装,UI 分别展示三种明确状态与原因,不再统一"重启后生效"。 +- `verifyActivation` 与 CLI reconcile 结果一致(读同一 manifest 字段),不存在"UI 说生效但 bundles 里没有"。 + +**用户收益**:装完立即知道"到底生效没有、为什么、还要做什么";"装上了但没生效"从玄学变成有因可查,避免无谓重启。 + +--- + +## P0-3 更新版本校验(含 minimumReleaseAge 说明与一键处理) + +**事故映射**:`/update` re-add 后只看 `exitCode === 0`,客户端就显示"✓ 已更新,重启后生效"。但 pnpm 新版本默认带发布年龄门槛 `minimumReleaseAge`(新发布版本会被解析器跳过;不同 pnpm 版本默认值不同——近版默认约 24h,且存在"新 major 被跳过不回退"的已知问题,见 [pnpm #10100](https://github.com/pnpm/pnpm/issues/10100));github 目标更新是 HEAD 重解析,上游无新提交时版本(commit)也不变。结果:**点了更新、显示成功、版本号没动**,用户以为功能坏了。 + +**改动位置**: +- `src/routes.ts` `/dsh-market/update`(+ 复用小工具 `readInstalledVersion`、`readLockCommits`); +- `client/client.js`:更新结果面板; +- 可选新路由 `/dsh-market/relax-minimum-release-age`(一键处理)。 + +**具体做法**: +1. **before/after 对比**:update 前 `before = readInstalledVersion(name)`(github 目标读 lockfile commit);成功后 `after`;`before === after` → 响应 `{ noChange: true, before, after }`,且客户端**绝不**显示"已更新"。 +2. **原因判定与提示**: + - npm 目标:查 npm registry 最新版 vs 当前版。存在更高版本 → 说明大概率被 `minimumReleaseAge` 挡住:展示"最新版本 vX.Y.Z(发布于 N 小时前)未在 npm 上新发布前被 pnpm 接受,受发布年龄门槛限制",并读当前门槛值(`pnpm config get minimum-release-age` / workspace 配置)一起展示;给"**放宽门槛并重试**"按钮 → 新路由在 `/pnpm-workspace.yaml` 写入 `minimumReleaseAge: 0`(保留原有内容)后重跑 update。不存在更高版本 → "已是最新版本 vX"。 + - github 目标:`readLockCommits` 的 HEAD commit 对比 → 无新提交时明确"上游无新提交(HEAD 仍为 abc1234)"。 +3. **更新提示前置**:`/updates` 列表在"npm 存在更高版本但会被策略跳过"时,徽标显示"有新版本(受发布年龄门槛限制)",而不是简单显示"已是最新"——让用户在点按钮之前就知道。 + +**验收标准**: +- 在设置 `minimumReleaseAge` 的 profile 中触发"有更高版本但被跳过"的更新:UI 明确显示版本未变 + 策略原因;点"放宽门槛并重试"后版本真正变化。 +- 无新版本时更新后显示"已是最新(vX)",绝不出现"✓ 已更新"。 +- github 目标无新提交时显示 commit 对比而不是"更新成功"。 + +**用户收益**:更新按钮结果可预期、可解释;"假更新"事故消失;被发布年龄策略挡住的用户有一键选项,不必去翻 pnpm 文档。 + +--- + +## P0-4 构建脚本放行 UI(allowBuilds + 一键重试) + +**事故映射**:pnpm v10+ 默认不执行依赖构建脚本;含原生依赖/`prepare` 脚本的插件(esbuild、node-gyp、git 安装包)**首次安装必失败**。stderr 里有 "Ignored build scripts: esbuild" 字样,但 UI 只显示一坨尾部文本;宿主 CLI 也只在 git 安装失败时打印一行文本提示("add the exact key pnpm printed above under allowBuilds in `/pnpm-workspace.yaml`")——用户要么看不懂,要么得手动编辑 yaml。 + +**改动位置**: +- `src/routes.ts`:新增 `/dsh-market/approve-builds` 路由 + stderr 解析函数; +- `client/client.js`:新内联组件 `BuildApprovalDialog`; +- 与 P1-9 的 `src/diagnose.ts` 共用解析。 + +**具体做法**: +1. **解析**(新 `parsePnpmFailure(stderr)`):正则捕获 `Ignored build scripts: (\S+(?:, \S+)*)` / `approve-builds` 字样 / `ERR_PNPM_*` 错误码 → `{ code, blockedBuilds: string[], hint }`。 +2. **对话框**:install 失败且 `blockedBuilds` 非空 → 弹出清单(`esbuild@0.25.x`、`node-gyp` 等)+ 说明"pnpm 默认禁止第三方构建脚本执行,这些包需批准后才会运行其构建",两个按钮: + - **"批准并重试"**:POST `/dsh-market/approve-builds`(body 携带原 install 的 url 或解析后的包清单)→ 服务端读-合并-写 `/pnpm-workspace.yaml` 的 `allowBuilds`(pnpm ≥11 键名;pnpm 10 为 `onlyBuiltDependencies`——按 `pnpm --version` 主版本选键,**保留 yaml 原有内容不覆盖**)→ 自动重发原 install 请求; + - "仅导出日志"。 +3. **安全**:写入的包名白名单校验(仅允许"目录条目解析出的目标包 + pnpm 报告的被拦包名"集合),拒绝任意字符串;写文件与安装日志走 `log.ts` 事件流(脱敏)。 + +**验收标准**: +- 安装一个带 esbuild/原生依赖的测试插件:失败后弹出清单对话框;点"批准并重试"后 `pnpm-workspace.yaml` 出现对应 allowBuilds/onlyBuiltDependencies 条目(原有内容保留),重试成功并进入生效提示。 +- pnpm 10 与 11 两种环境下写入正确的键名并再次安装通过。 + +**用户收益**:最常见的第一安装失败从"看不懂的报错"变成"一个按钮解决",无需手写 yaml;与 P1-5 的错误面板共用解析,一处识别多处受益。 + +--- + +# P1 —— 强烈建议 + +## P1-5 错误呈现:stderr 关键行 + 错误旁导出日志 + +**事故映射**:当前错误 = `error || stderr || stdout` 尾部 600 字符塞进一行(client.js `installError`);stderr 在路由里只留 64KB 尾部;"导出日志"入口只在页头小字链接,出错现场没有。 + +**改动位置**: +- `src/routes.ts`:`runDshPlugin` 增加 `lastErrorLines`(stderr 尾部 ~40 行)与 `diagnosis`;输出缓冲上限提高; +- 新 `src/diagnose.ts`(错误分类,与 P1-9 合并实现); +- `client/client.js`:新内联组件 `ErrorPanel` 替代单行错误文本。 + +**具体做法**: +1. **路由侧**:失败时 `diagnosis = { code, summary, keyLines: string[], blockedBuilds?, actionType? }`;stdout/stderr 环形缓冲扩大到 256KB 尾部;完整输出可经 `/dsh-market/logs` 导出(复用 `log.ts` 脱敏:路径→`~`、密钥掩码)。 +2. **ErrorPanel**: + - 首行:错误码 + 一句话结论("解析依赖失败 / 网络超时 / 构建脚本被拦 / 目标包不存在"); + - 折叠的"关键行"块(≤10 行); + - "完整输出"可展开(stdout/stderr 尾部原文); + - 按钮组:**复制**、**导出日志**(下载 `/dsh-market/logs`,导出文件头带 profile、目标、pnpm 版本、本次操作参数)。 +3. **i18n**:新增文案进 zh/en 词典;导出文件头沿用 `log.ts` 的 `exportLogs` 格式扩展。 + +**验收标准**: +- 人为制造三类失败(断网、包不存在、构建脚本被拦),UI 分别显示对应错误码 + 结论 + 关键行,一键复制/导出包含上下文的日志。 +- 导出内容不含明文密钥与绝对 home 路径(沿用现有 sanitize 并加回归用例)。 + +**用户收益**:报错能看懂、能复现、能一键提交给维护者;支持成本显著下降。 + +--- + +## P1-6 进度/取消:真实阶段进度(ndjson) + +**事故映射**:现有进度轮询 `/status` 的 `lastLine` 并解析 pnpm "Progress: resolved..." 聚合行(`parseProgressPct`);CI 模式下 pnpm 非 TTY 输出往往不吐该行 → 用户长时间只看到"安装中…",不知道是卡住还是在进行;阶段(解析/下载/链接/克隆)不可见。取消已有(killTree)但无"正在取消"中间态,取消后依赖可能半写,状态回执简单。 + +**改动位置**: +- `src/routes.ts`:`runDshPlugin` 追加 `--reporter=ndjson`(pnpm 原生选项,进度事件见 [pnpm issue #3822](https://github.com/pnpm/pnpm/issues/3822)),`/status` 响应升级;`/cancel` 增加"取消中"态; +- `client/client.js`:进度条升级。 + +**具体做法**: +1. **结构化进度**:以 `--reporter=ndjson` 追加到 pnpm 参数(`dsh plugin add --reporter=ndjson`,pnpm 全局选项),解析事件流维护内存进度: + - `progress` 事件 → `{ phase: 'resolving' | 'downloading' | 'linking', resolved, resolvedTotal }`; + - `fetchingProgress` 事件 → 当前下载包名 + 字节进度; + - 旧 pnpm 无 ndjson 事件 → 回退现有行解析 + 阶段猜测(并记录一条 warn 日志便于升级建议)。 + - 实施前先用 CI 模式实测事件流形态(含失败时的 error 事件),解析器带未知事件容错。 +2. **/status 升级**:`{ phase, done, total, currentPackage, seconds, active, cancelled }`(保留 `lastLine` 兼容旧客户端)。 +3. **客户端进度条**:阶段徽标(解析依赖 → 下载 → 链接)+ 当前包名 + N/M + 百分比;git 克隆阶段显示"克隆仓库…"。 +4. **取消**:点击 → "正在取消…"(disable 防重复);killTree 后回执 `{ cancelled, partial }`;客户端对比 manifest before/after 展示真实残留状态("已取消,部分依赖已写入/已回滚"),并清理 sessionStorage 的 pending 标记。 + +**验收标准**: +- 安装 30+ 依赖的插件:进度条显示 解析/下载/链接 阶段与 N/M,不再长时间"卡在安装中"。 +- 下载中途取消:≤3s 内显示取消确认;profile 依赖状态与文件系统一致(无幽灵依赖)。 + +**用户收益**:安装过程透明可预期;误点或等太久可随时安全取消,取消后果明确。 + +--- + +## P1-7 安装目标锁定 commit(可复现安装) + +**事故映射**:git 目标当前不锁 commit,重复安装/重建 profile 结果不可复现;"更新"语义模糊(HEAD 移动即算更新)。`readLockCommits` 已能从 lockfile 读出 codeload 的 40 位 sha——锁已隐式存在,只是没写进安装命令。 + +**改动位置**:`src/routes.ts` 的 `resolveTarget` / `/install`;`/updates` 复用已有 commit 对比。 + +**具体做法**: +1. install 时先取 `https://api.github.com/repos//commits/HEAD`(已有 `fetchJson`,复用 4s 超时 + 缓存),目标写 `github:owner/repo#`(subpath 场景同样带 sha)。 +2. "更新"按钮语义 = "把锁移到最新 HEAD",更新前后展示 commit 对比(old sha → new sha 前 8 位),与 P0-3 的 noChange 判定共用。 + +**验收标准**:同一条目装两次,lockfile 中 codeload tarball URL 完全一致;更新按钮前后 commit 明确展示;无新提交时显示"上游无新提交"而非"更新成功"。 + +**用户收益**:可复现安装;更新有明确的"版本变化"证据;为 P0-3 的 github 分支提供可靠数据。 + +--- + +## P1-8 目录数据治理 + +**事故映射**:目录是手工维护的 snapshot(237 条),`npm` 字段可选且无存在性校验;npm 名过期/拼错会直接装错或装失败;TTL 1h 缓存 + 离线快照没有"目录更新于 X"的提示,前端也无法手动刷新。 + +**改动位置**:`scripts/preflight.mjs`(发布 gate)、新 `scripts/validate-registry.mjs`(CI/维护)、`src/registry.ts`(force 刷新)、`client/client.js`。 + +**具体做法**: +1. **preflight 扩展**:每条 entry——`url` 必须 GitHub、`subpath` 无 `..`/绝对路径、`npm`/`package`/`subpath` 互斥约束、`install` 命令与 `resolveTarget` 解析结果一致、name 不重复;`npm run prepack` 已挂钩,顺带生效。 +2. **新校验脚本**:对全部条目做轻量探测(npm 条目查 registry metadata;git 条目查 repo 根 `package.json` 是否存在),输出"不可装/待修复"清单,与 snapshot 一起提交。 +3. **前端**:展示目录 `updated` 日期 + "目录已刷新(x 分钟前)";页头加"刷新目录"(GET `/dsh-market/registry?force=1` 绕过 TTL);`uninstallable`/探测失败的条目置灰并显示原因。 + +**验收标准**:目录更新流程跑校验零报错;前端可看到更新时间并可手动刷新;失效条目不再出现可点的"安装"按钮。 + +**用户收益**:目录可信度高,坏条目不会出现在用户面前浪费一次安装时间。 + +--- + +## P1-9 结构化诊断与操作建议(错误分类服务) + +**事故映射**:错误处理散落在路由与客户端,识别逻辑重复;同一类错误(如构建脚本被拦)在安装、更新两个入口表现不一致。 + +**改动位置**:新 `src/diagnose.ts`(P1-5 与 P0-4 的共用底座)。 + +**具体做法**:把 pnpm 常见失败映射到 `{ message, action, actionType }`: + +| pnpm 错误形态 | 建议动作 | actionType | +|---|---|---| +| `Ignored build scripts:` | 批准构建脚本 | `approve-builds`(→ P0-4 对话框) | +| `ERR_PNPM_NO_MATCHING_VERSION` / 包不存在 | 目标包不存在,更新目录 | `manual`(附链接) | +| `ERR_PNPM_FETCH_*` / `EAI_AGAIN` / 网络超时 | 重试 / 检查网络 | `retry` | +| 更新后版本未变 + npm 有更高版 | 放宽 minimumReleaseAge | `relax-minimum-release-age`(→ P0-3) | +| git 克隆失败 / 子目录语法不支持 | 手动安装或报告目录条目 | `manual` | +| 未识别错误 | 导出日志反馈 | `export-log`(→ P1-5) | + +install/update/uninstall 响应统一带 `diagnosis`;客户端错误面板按 `actionType` 渲染对应按钮。验收:10 类常见错误各有一个可执行建议;未识别错误一律回退"导出日志"。 + +--- + +# P2 —— 锦上添花 + +## P2-10 目标 profile 选择 +- **改动位置**:新 `/dsh-market/profiles` 路由 + client 下拉。 +- **做法**:install 目前固定 `config.profile`(web)。增加"安装到"下拉(web / headless / 用户已有 profile),terminal 类插件默认提示装到 headless(与现有 `looksTerminal` 合并);服务端保持安全边界:只允许 curated registry + 本机 profile 白名单。 +- **验收**:终端类插件可一键装进 headless profile 并生效。 +- **收益**:CLI 类插件有正确去处,不再污染 web profile。 + +## P2-11 安装台账(journal) +- **改动位置**:`src/log.ts` 扩展(内存事件流 → profile 下 `.dsh-market/journal.jsonl`,脱敏持久化)+ 新只读路由。 +- **做法**:"已安装"页显示"何时装的、装了什么目标、当时版本",导出日志附带台账。 +- **验收**:重装/更新后台账可查历史版本与时间。 +- **收益**:可审计、可回溯,出问题能定位"哪次操作导致的"。 + +## P2-12 断线恢复与幂等增强 +- **改动位置**:`/install` 路由 + client。 +- **做法**:已有 sessionStorage pending 恢复(client.js `busyUrl` 恢复);增强为 install 请求带幂等键(目标+commit),路由对重复请求返回既有结果;轮询恢复时显示"上次安装可能已完成,正在确认…"。 +- **验收**:安装中途刷新页面,恢复后状态收敛到真实结果。 +- **收益**:刷新/断网不丢状态、不重复安装。 + +## P2-13 打磨 +- **做法**:Esc 关闭弹窗、焦点管理、aria 标签;排序/搜索增强(关键词高亮);新组件配色沿用现有 CSS 变量(暗色主题自适应);237 条卡片列表虚拟化(可选)。 +- **验收**:键盘可完成"搜索→安装→取消"全流程;暗色主题下无死色块。 +- **收益**:体验完整度。 + +--- + +## 实施顺序建议 + +**阶段 A —— 判定与数据层先行(不依赖 UI,立刻提升可信度)** +1. **P0-1 的 registry schema 扩展 + preflight 校验**(纯数据 + `scripts/preflight.mjs`,零 UI 依赖); +2. **P0-3 的 before/after 版本对比**(纯服务端判定,客户端只改一处文案分支); +3. **P0-2 的 `verifyActivation`**(新模块,install/update/uninstall 三处挂响应字段)。 +> 理由:这三件是"结果正确性"的地基,且**全部是服务端/数据侧改动**,UI 的呈现都是消费它们的判定结果;先做地基,后续 UI 一次成型、不用返工。三者相互独立,可并行分派。 + +**阶段 B —— 正确性落地(消灭三大事故)** +4. P0-1 目标解析 + 前置校验接入 `/install`(含 git 子目录语法实测); +5. P0-2 客户端四态呈现; +6. P0-3 客户端 noChange 面板 + "放宽门槛并重试"。 +> 理由:这三件直接消灭"装错目标、装完不知道生效没有、假更新"三个最高频事故,先于任何体验优化。 + +**阶段 C —— 可操作性(让失败可自助解决)** +7. **P1-9 diagnosis 服务**(P0-4 与 P1-5 的共同底座,先于两者); +8. **P0-4 构建脚本放行对话框**(依赖 7 的解析 + 需要新路由); +9. **P1-5 ErrorPanel**(依赖 7)。 +> 理由:构建脚本被拦是最常见首装失败,但"一键批准"必须建立在可靠的错误解析上,所以 diagnosis 先行;ErrorPanel 复用同一解析。 + +**阶段 D —— 过程透明** +10. **P1-6 ndjson 进度/取消**(需要真实安装回归验证事件流); +11. **P1-7 commit 锁定**(改安装参数,必须与 10 一起回归主安装链路)。 +> 理由:两者都触碰安装主链路,一起做可共享一次完整的安装回归测试。 + +**阶段 E —— 治理与打磨** +12. P1-8 目录校验脚本、P2-10/11/12/13 各项。 +> 理由:全部是增量优化,不阻塞主链路;放在最后避免与主链路改动互相干扰。 + +**贯穿原则**:每个阶段结束都跑一遍主链路回归(安装真实插件 → 校验 bundles → 热挂/重启 → 更新 → 卸载),保持 `scripts/preflight.mjs` 与 `npm run check` 绿;客户端改动坚持内联子组件、不引入构建链(避免破坏 `preflight` 对 loader id 的校验);所有新增文案进 zh/en 双词典。 + +--- + +## 事实参考 +- pnpm git 依赖子目录支持:[pnpm/pnpm#7487](https://github.com/pnpm/pnpm/pull/7487)、[pnpm Discussion #8148](https://github.com/orgs/pnpm/discussions/8148)、[pnpm 支持包来源文档](https://www.pnpm.cn/id/package-sources) +- `minimumReleaseAge` 发布年龄门槛与已知问题:[pnpm/pnpm#10100](https://github.com/pnpm/pnpm/issues/10100)、[pnpm CHANGELOG](https://github.com/pnpm/pnpm/blob/a751c7f2/pnpm/CHANGELOG.md?plain=1#L37-L45) +- 构建脚本放行字段演进(`onlyBuiltDependencies` → `allowBuilds`):[pnpm/pnpm#11209](https://github.com/pnpm/pnpm/pull/11209)、[pnpm/pnpm#10235](https://github.com/pnpm/pnpm/issues/10235)、[pnpm v10→v11 迁移](https://pnpm.io/migration) +- ndjson 进度事件:[pnpm/pnpm#3822](https://github.com/pnpm/pnpm/issues/3822)、[pnpm/pnpm#5380](https://github.com/pnpm/pnpm/issues/5380) diff --git a/packages/dshmarket/LICENSE b/packages/dshmarket/LICENSE new file mode 100644 index 000000000..6d4139216 --- /dev/null +++ b/packages/dshmarket/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 fkysly and dsh-market contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/dshmarket/README.md b/packages/dshmarket/README.md new file mode 100644 index 000000000..ec5f2dc4f --- /dev/null +++ b/packages/dshmarket/README.md @@ -0,0 +1,136 @@ +

+ dsh-market logo +

+ +# dsh-market + +English | [中文](README.zh.md) + +[![npm](https://img.shields.io/npm/v/dshmarket)](https://www.npmjs.com/package/dshmarket) +[![stars](https://img.shields.io/github/stars/dsh-market/dsh-market?style=flat)](https://github.com/dsh-market/dsh-market) + +The plugin market inside DeepSeek Harness. Open Settings → **Plugin Market** → browse, search, one-click install. + +![dsh-market](assets/demo-en.png) + +One-click themes: install, switch live, no restart. + +## Install + +```sh +dsh plugin --profile web add dshmarket +``` + +Restart `dsh web`, then open **Settings → Plugin Market**. + +**Requires dsh web 0.1.0-rc.6 or newer.** On an older host the market +disables itself and says so in the browser console rather than rendering +against primitives that are not there — if the Plugin Market entry never +appears, that is usually why. Worth checking when a desktop build bundles +its own dsh: it may be older than the one `npm` would give you (#139). + +## What you get + +- **Browse & search** the full community catalog (2300+ plugins, growing daily) — category filters, star counts, top/new sorting, bilingual descriptions that follow your UI language +- **Screenshots** — AppStore-style screenshots, auto-carousel when there's more than one, click to preview full-size: author-curated shots show right on the card (zero extra requests); plugins without curated shots fall back to automatic README extraction once you open the install dialog. Images load from GitHub hosting only +- **Comments** — every card opens the plugin's discussion thread in place. It is the same thread its pages on [dshmarket.com](https://dshmarket.com) and the [catalog](https://awesome-dsh-plugin.com) show, so a plugin has one conversation rather than three. Backed by GitHub Discussions through giscus: it loads when you open it, needs a GitHub account only to post, and the note above it says plainly that opening it contacts giscus.app and GitHub +- **Themes** — a dedicated tab for community themes and skins: install → active immediately, switch with one click (themes are mutually exclusive, your choice survives restarts), uninstall to revert +- **One-click install** — confirm the source, watch live progress; most plugins go live after a page refresh, no restart +- **Backup & restore** — export your profile's plugin list and configuration as readable JSON, import it on another machine, store it on WebDAV with daily auto-backup, or sync through a private GitHub Gist; restores **merge** (plugins installed after the backup are kept), validate before writing, and roll back on failure +- **Updates** — per-plugin update checks (npm version or pinned commit vs HEAD), one-click update, or update everything at once; the market updates itself the same way +- **Public update API** — plugin-owned settings pages can use the versioned, capability-gated [update API v1](UPDATE-API-V1.md) (beta) instead of copying package-manager logic or depending on private Market UI responses +- **Uninstall** — two-step confirm; plugins installed this session are removed live +- **Hot disable / enable** — toggles write `- id: …` + `disabled: true|false` into the profile's `cordis.patch.yml` (the official patch layer, mechanism ported from [dsh-plugin-hub](https://github.com/Noob-stupid/dsh-plugin-hub)): DSH's HMR re-composes within ~1s, no restart, and the loader re-applies the choice on every boot; hand-edited patch rows show as badges, host-infrastructure plugins are protected from toggling, and a malformed patch file is never made worse +- **Restart when needed** — changes that cannot hot-load show a one-click restart beside the pending-change banner; the action is restricted to same-origin loopback requests +- **Zero jargon** — if a component is missing (pnpm), the market detects it and offers a one-click automatic setup +- **Log export** — one click produces a sanitized plain-text log for bug reports (home paths and credential shapes are masked; nothing is ever sent anywhere). The market's version sits next to the page heading, so a screenshot of a problem already carries it +- **Settings card** — on dsh 0.1.0-rc.7 and newer the market manages *itself* from **Settings → Plugins → Plugin configuration**, next to every other plugin: see the running version, pick a **release channel** (stable, or beta to try builds still being verified — the market only, never your other plugins; a third *dev* channel appears once developer mode is switched on, and carries builds published straight off a branch), update, or remove the market — with an opt-in cleanup that also drops the disable rows it wrote, so plugins it switched off start running again rather than staying off with no UI left to switch them back on +- **Diagnostics** — the plugin load order and conflict surface, one page: bundle stack with official/community badges, duplicate loader entries, dependency version mismatches, multi-version core packages, overrides and invalid config entries. Plain-language terms, problem blocks highlighted, everything collapsible + +- **Load order** — drag community bundles into the order you want, or take the suggested one derived from the plugins' own before/after rules. Nothing is written until a trial composition passes, and the panel tells you what the new order would change (overrides, invalid or duplicate entries) before you apply it +- **AI fix** — one click copies a diagnostics-driven fix prompt (errors/warnings/order conflicts + conservative scope instructions) to the clipboard; you paste it into a new conversation and decide whether to send. The prompt first asks the agent to detect whether it is itself the harness running this profile — if so it hard-forbids mutating the live composition, upgrading/restarting the harness or core packages, or reinstalling deps, and instead has it write an idempotent `apply` script plus a `rollback` script, have you run them in an external terminal, and paste the output back + +## Speed + +Installs prefer repo-verified npm packages, then author-supplied prebuilt GitHub Release tarballs, before falling back to full-repo GitHub source downloads. Prebuilt installs are typically seconds and do not need local build scripts; source-only plugins depend on your connection to GitHub. + +## Security + +- Installs are restricted to sources listed in the curated [awesome-dsh-plugin](https://awesome-dsh-plugin.com) registry — anything else is rejected +- Build scripts stay blocked by default (pnpm ≥10); allowing one is your explicit per-package choice +- Terminal/CLI-surface plugins are flagged before you install them into the web profile +- The install endpoint accepts same-origin POST only; the market never phones home +- Backups can contain credentials from your profile config — the UI warns before export and upload; WebDAV sync is https-only, refuses private-network targets, and never stores your password in the browser +- The restart endpoint additionally requires a direct loopback client (forwarded requests are rejected) and relaunches the exact DSH entry, arguments, environment, and working directory +- One-click restart launches a detached replacement. **When this host is systemd's own service process the button is hidden automatically** — the market would otherwise kill the takeover process along with the unit's cgroup and the service would not come back. The pending-change notice stays visible and says so. Detection requires both a systemd marker AND being the unit's main process, because `INVOCATION_ID` is inherited by every descendant of a unit (an ordinary terminal included) and hiding the button for those would be the worse bug. pm2 and launchd are not detected, so those deployments need the explicit setting below. Either flip **Allow restart** off in **Settings → Plugins → Plugin configuration**, or write it into the profile patch — where it has to sit under `config:`, because the loader passes only that sub-object to a plugin and a top-level `allowRestart:` is silently ignored (#227 by @Fantasymax): + + ```yaml + - id: dsh-market + name: dshmarket + config: + allowRestart: false # NOT at the top level beside `name:` + ``` + + `GET /dsh-market/status` reports `"restart": false` once it has taken effect. +- For terminal-attached launches, the detached replacement keeps running after the original terminal closes +- Listing ≠ endorsement: plugins are third-party code, install sources you trust + +## Submit your plugin + +**This repo is the market app, not the catalog.** The plugin list comes from the curated [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) registry — to get your plugin listed in the market, open a PR **there** (one entry in the list; the site and this market pick it up automatically, usually within a day). Please don't PR plugin entries against this repo. + +## Roadmap & feedback + +- **Bugs** go in [issues](https://github.com/dsh-market/dsh-market/issues) — attaching the market's "Export log" makes diagnosis roughly ten times faster +- **Feature ideas** go on the [Roadmap](https://github.com/orgs/dsh-market/projects/1). Issues are kept for things that are broken, so a proposal filed as an issue gets moved there and closed; the discussion stays where you wrote it either way +- Every roadmap item welcomes community PRs — say so on the item before starting, so two people don't build it twice + +## Data source + +Fetched live on every open from [awesome-dsh-plugin.com/plugins.json](https://awesome-dsh-plugin.com/plugins.json) — curated entries, npm mapping, and star counts refreshed daily by CI, with no stale cache behind it. A failure reports the actual reason and elapsed time, with a Retry button. + +There is deliberately no bundled snapshot to fall back on: for a catalog that grows daily, a stale answer is not a degraded one but a wrong one — a plugin published this morning would read as "does not exist". + +**If that host is unreachable from your network**, point the market at a mirror instead. Set `DSHM_REGISTRY_URL` in the environment dsh runs in, to anything serving the same `plugins.json` shape: + +```sh +DSHM_REGISTRY_URL=https://your-mirror.example/plugins.json dsh web +``` + +## Friends + +### DSH Desktop (dataelement) + +[dsh-desktop](https://github.com/dataelement/dsh-desktop) — a desktop app for DeepSeek Harness: run and manage a local Harness without installing Node.js yourself. Ships with this plugin market preset as the default. [dshdesktop.com](https://dshdesktop.com) + +### DeepSeek Harness Desktop (hairyf) + +[deepseek-harness-desktop](https://github.com/hairyf/deepseek-harness-desktop) — a native desktop app for DeepSeek Harness built with **Tauri** (Rust + Web): one-click local install and launch with no Node.js setup required. On first run it offers to install this plugin market as a recommended preset. + +### DeepSeek Harness Desktop (anywhere-labs) + +[deepseek-harness-desktop](https://github.com/anywhere-labs/deepseek-harness-desktop) — an Electron desktop app for DeepSeek Harness built around the idea that everything is a plugin, including the desktop itself: profile switching, a bundled Node and pnpm, and a recoverable install path that snapshots the profile before a change. [dshdesktop.cn](https://dshdesktop.cn) + +### DSH App + +[dsh-app](https://github.com/RyensX/dsh-app) — a DeepSeek Harness desktop client built on Tauri 2 rather than Electron, so it ships a much smaller binary and uses the system webview. AGPL-3.0. + +### Local DSH + +[local-dsh](https://github.com/liangchen-harold/local-dsh) — a DeepSeek Harness desktop client that can run the model on your own machine: it bundles llama.cpp next to Node, pnpm and DSH, so a downloaded GGUF model answers without any external API. Built on Tauri; Apple Silicon Macs for now. [localdsh.com](https://localdsh.com) + +### DSH Get + +[DSH Get](https://www.dshget.com/) — a searchable web directory for discovering DeepSeek Harness plugins: category filters, bilingual descriptions, install commands and per-plugin detail pages. Its normalized catalog snapshot is public at [bobby-sheng/dshget-data](https://github.com/bobby-sheng/dshget-data). + +### modlens + +[modlens](https://github.com/liustack/modlens) — the first vision plugin for DeepSeek Harness: bolts visual understanding onto text-only models like DeepSeek and GLM. Paste an image, get structured JSON evidence back — OCR, layout, semantics. Available right in this market: + +```sh +dsh plugin --profile web add @liustack/modlens +``` + +## License + +MIT · [dshmarket.com](https://dshmarket.com) diff --git a/packages/dshmarket/README.zh.md b/packages/dshmarket/README.zh.md new file mode 100644 index 000000000..d68c88079 --- /dev/null +++ b/packages/dshmarket/README.zh.md @@ -0,0 +1,133 @@ +

+ dsh-market logo +

+ +# dsh-market + +[English](README.md) | 中文 + +[![npm](https://img.shields.io/npm/v/dshmarket)](https://www.npmjs.com/package/dshmarket) +[![stars](https://img.shields.io/github/stars/dsh-market/dsh-market?style=flat)](https://github.com/dsh-market/dsh-market) + +装在 DeepSeek Harness 里的插件市场。打开设置 → **插件市场** → 逛一逛,点一下,装好。 + +![dsh-market](assets/demo-zh.png) + +主题一键换:装完即生效,点一下切换,不用重启。 + +## 安装 + +```sh +dsh plugin --profile web add dshmarket +``` + +重启 `dsh web`,打开 **设置 → 插件市场**。 + +**需要 dsh web 0.1.0-rc.6 或更新版本。** 宿主太旧时市场会自我禁用,并在浏览器 +控制台说明原因,而不是拿缺失的原语去渲染——如果设置里根本没出现「插件市场」这 +一项,通常就是这个原因。桌面端要留意:它可能内置了比 `npm` 装到的更旧的 dsh(#139)。 + +## 你会得到 + +- **逛与搜**——完整社区目录(2300+ 插件,每天在涨),分类筛选、star 数、最热/最新排序,中英描述跟随界面语言 +- **截图展示**——App Store 式截图,多图自动轮播,点开还能看大图;作者在 registry 里策展的截图列表卡片就直接显示(零额外请求),没有策展的插件则在打开安装弹窗时自动从 README 抽取;图片仅从 GitHub 图床加载 +- **评论**——每张卡片都能就地打开该插件的讨论。它和插件在 [dshmarket.com](https://dshmarket.com) 与[目录站](https://awesome-dsh-plugin.com)上的页面共用同一条讨论,一个插件只有一处对话,而不是三处。底层是 GitHub Discussions(经由 giscus):打开即加载,只有发表评论才需要 GitHub 账号;说明里也直说打开会连接 giscus.app 与 GitHub +- **主题**——独立主题页:装完立即生效,点一下切换(主题互斥、选择跨重启保留),卸载即恢复 +- **一键安装**——确认来源,实时进度;多数插件刷新页面即可用,无需重启 +- **备份与恢复**——把 profile 的插件清单与配置导出为可读 JSON,换机导入,存到 WebDAV 并每日自动备份,或通过私有 GitHub Gist 跨机器同步;恢复采用**合并**方式(备份之后新装的插件会保留),写入前校验、失败自动回滚 +- **更新**——逐插件检测(npm 版本或锁定 commit 对比 HEAD),一键更新或全部更新;市场自己也走同一通道升级 +- **公共更新接口**——插件自己的设置页可调用带版本号、能力探测和回滚状态的[更新 API v1](UPDATE-API-V1.md)(beta),无需复制包管理逻辑,也不依赖市场 UI 的私有响应字段 +- **卸载**——两步确认防误触;本次会话装的插件即点即卸 +- **热禁用 / 启用**——开关会往 profile 的 `cordis.patch.yml`(官方补丁层,机制移植自 [dsh-plugin-hub](https://github.com/Noob-stupid/dsh-plugin-hub))写入 `- id: …` + `disabled: true|false`:DSH 的 HMR 约 1 秒内重新组合,无需重启,loader 每次启动都会重新应用这个选择;手工改过的补丁行会显示成徽标,宿主基础设施插件禁止开关,补丁文件格式不对时绝不会被写得更糟 +- **按需重启**——无法热加载的变更会在待重启提示旁显示一键重启;操作仅接受本机同源请求 +- **零术语**——缺组件(pnpm)时市场自己发现、一键自动装好,全程不见命令行 +- **导出日志**——一键生成脱敏纯文本日志方便反馈(home 路径与密钥形状已打码;任何数据都不会被上传)。市场版本号就在标题旁边,截图反馈时自带版本信息 +- **设置卡片**——dsh 0.1.0-rc.7 起,市场在 **设置 → 插件 → 插件配置** 里管理**它自己**,和其它插件并排:看当前版本、选择**更新通道**(稳定版,或 Beta 抢先试用还在验证中的版本——只影响市场自己,不影响你装的其它插件;打开开发者模式后还会多出「开发版」通道,直接取开发分支上的构建)、更新、或者移除市场。移除时可勾选一并清理——包括市场写进补丁层的停用行,被它关掉的插件会恢复运行,而不是保持停用却再没有界面能打开它们 +- **诊断**——插件加载顺序与冲突一页看全:bundle 栈(官方/社区徽标)、重复的 loader 条目、依赖版本不一致、核心包多版本共存、覆盖项与非法配置条目。术语说人话,问题块高亮,全部可折叠 +- **加载顺序**——拖拽调整社区 bundle 的顺序,或直接采用按插件自身 before/after 规则推导出的建议顺序。写入前先跑一次静态组合校验,通过才落盘;应用之前面板会告诉你这个顺序会改变什么(多少处覆盖、多少无效或重复条目) +- **AI 修复**——一键把诊断结果生成的修复 prompt(错误/警告/顺序冲突 + 保守的改动范围约束)复制到剪贴板,你粘进新对话,自己决定发不发。提示词会先让 Agent 判断自己是不是就是这个 profile 所在的 harness——若是,则严禁改动运行中组合、升级/重启 harness 或核心包、重装依赖,而是让它生成一份幂等的 `apply` 修改脚本和一份 `rollback` 回滚脚本,由你在外部终端执行并把输出拷回给它 + +## 速度 + +安装依次优先使用经仓库验证的 npm 包、作者提供的 GitHub Release 预构建 tarball,最后才回退到整仓 GitHub 源码下载。预构建安装通常只需数秒且无需执行本地构建脚本;仅提供源码的插件仍取决于你到 GitHub 的网络。 + +## 安全 + +- 只允许安装 [awesome-dsh-plugin](https://awesome-dsh-plugin.com) 精选列表内的来源,其它一律拒绝 +- 构建脚本默认禁止执行(pnpm ≥10),放行与否由你按包显式决定 +- 终端/命令行类插件装进网页版前会被明确提醒 +- 安装接口只接受同源 POST;市场不会向任何地方上报数据 +- 备份可能包含 profile 配置里的密钥——导出与上传前 UI 会明确提醒;WebDAV 同步仅限 https、拒绝内网地址,且密码永不落盘浏览器 +- 重启接口还要求客户端直接来自环回地址(拒绝代理转发请求),并使用原入口、参数、环境和工作目录重新启动 DSH +- 一键重启会启动脱离终端的替代进程。**当本进程就是 systemd 服务的主进程时,按钮会自动隐藏**——否则市场重启会连带杀掉 cgroup 里的接管进程,服务起不来,待重启提示会说明原因。判定要求「systemd 标记」和「本进程是该 unit 的主进程」同时成立:`INVOCATION_ID` 会被 unit 的所有后代继承(包括普通终端),只看它会误伤一大批本来能正常重启的机器。pm2 和 launchd 不做检测,这类部署需要下面的显式配置。两种做法:在**设置 → 插件 → 插件配置**里关掉「允许重启」,或者写进 profile 补丁——注意必须嵌在 `config:` 下面,因为 loader 只把这个子对象传给插件,写在顶层会静默失效(#227,感谢 @Fantasymax): + + ```yaml + - id: dsh-market + name: dshmarket + config: + allowRestart: false # 不要和 `name:` 并排写在顶层 + ``` + + 生效后 `GET /dsh-market/status` 会返回 `"restart": false`。 +- 从终端启动时,替代进程脱离原终端,关闭原终端后仍会继续运行 +- 收录 ≠ 背书:插件是第三方代码,请只安装你信任的来源 + +## 提交你的插件 + +**这个仓库是市场应用本身,不是插件目录。** 市场里的插件列表来自精选列表 [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)——想让你的插件上架,请去**那边**提 PR(在列表里加一条即可,站点和本市场会自动收录,通常一天内生效)。请不要往本仓库提插件条目。 + +## 路线图与反馈 + +- **Bug** 提 [issue](https://github.com/dsh-market/dsh-market/issues),附上市场页面的「导出日志」能让排查快十倍 +- **功能建议**放 [Roadmap](https://github.com/orgs/dsh-market/projects/1)。issues 只留「坏掉的东西」,所以提成 issue 的建议会被移到那边并关闭;讨论仍留在你写的地方 +- 路线图上的每一项都欢迎社区 PR——动手前在对应条目里说一声,免得两个人重复造 + +## 数据源 + +每次打开都实时请求 [awesome-dsh-plugin.com/plugins.json](https://awesome-dsh-plugin.com/plugins.json)——精选条目、npm 映射、star 数由 CI 每日刷新,不使用过期缓存兜底;连不上时会给出具体原因和耗时,并提供「重试」按钮。 + +刻意不做本地快照兜底:目录每天都在增长,过期的答案不是「差一点」而是「错的」——今早刚发布的插件会显示成「不存在」。 + +**如果你的网络访问不了这个域名**,可以改指到镜像:在 dsh 运行的环境里设置 `DSHM_REGISTRY_URL`,指向任何提供相同 `plugins.json` 结构的地址: + +```sh +DSHM_REGISTRY_URL=https://your-mirror.example/plugins.json dsh web +``` + +## 友情链接 + +### DSH Desktop(dataelement) + +[dsh-desktop](https://github.com/dataelement/dsh-desktop)——DeepSeek Harness 桌面客户端:无需自装 Node.js 即可运行和管理本地 Harness,并默认预置本插件市场。[dshdesktop.com](https://dshdesktop.com) + +### DeepSeek Harness Desktop(hairyf) + +[deepseek-harness-desktop](https://github.com/hairyf/deepseek-harness-desktop)——基于 **Tauri**(Rust + Web)构建的 DeepSeek Harness 原生桌面客户端:一键本地安装并启动,无需自装 Node.js;首次启动可选择安装本插件市场作为推荐插件。 + +### DeepSeek Harness Desktop(anywhere-labs) + +[deepseek-harness-desktop](https://github.com/anywhere-labs/deepseek-harness-desktop)——基于 Electron 的 DeepSeek Harness 桌面客户端,理念是「万物皆插件,桌面本身也是插件」:支持 profile 切换、内置 Node 与 pnpm,安装前会先给 profile 拍快照以便回滚。[dshdesktop.cn](https://dshdesktop.cn) + +### DSH App + +[dsh-app](https://github.com/RyensX/dsh-app)——基于 Tauri 2(而非 Electron)的 DeepSeek Harness 桌面客户端,因此安装包小得多,界面走系统 webview。AGPL-3.0。 + +### Local DSH + +[local-dsh](https://github.com/liangchen-harold/local-dsh)——可以把模型跑在本机的 DeepSeek Harness 桌面客户端:发行包内置 llama.cpp 与 Node、pnpm、DSH,下载一个 GGUF 模型就能对话,不必接外部 API。基于 Tauri 构建;目前支持 Apple 芯片的 Mac。[localdsh.com](https://localdsh.com) + +### DSH Get + +[DSH Get](https://www.dshget.com/)——DeepSeek Harness 插件的网页检索目录:分类筛选、中英描述、安装命令与插件详情页;其规范化的目录快照公开在 [bobby-sheng/dshget-data](https://github.com/bobby-sheng/dshget-data)。 + +### modlens + +[modlens](https://github.com/liustack/modlens)——全网第一个 DeepSeek Harness 视觉插件,为 DeepSeek、GLM 等纯文本模型外挂视觉能力,粘贴图片即得结构化 JSON 证据(OCR、版面、语义)。本市场内即可直接安装: + +```sh +dsh plugin --profile web add @liustack/modlens +``` + +## 许可 + +MIT · [dshmarket.com](https://dshmarket.com) diff --git a/packages/dshmarket/TESTING.md b/packages/dshmarket/TESTING.md new file mode 100644 index 000000000..c248db31d --- /dev/null +++ b/packages/dshmarket/TESTING.md @@ -0,0 +1,127 @@ +# Testing + +Four layers, mirroring the deepseek-harness conventions — everything runs on +vitest; playwright is used as a library, never as a separate runner. + +| Layer | What | Where | Command | CI | +|---|---|---|---|---| +| 1. Behavioral specs | Pure/orchestration logic against real tmp-dir fixtures; flow suite drives every HTTP route through a programmable FakeDsh (real filesystem effects, scriptable npm state — update logic is testable **without publishing versions**) | `tests/*.spec.ts` | `npm test` | every push, ubuntu + windows | +| 2. Component specs | `// @vitest-environment jsdom` + testing-library against the REAL TSX components, REAL locale dicts, and the npm-published `@deepseek-ai/dsh-client-ui-primitives` | `tests/client/*.client.spec.tsx` | `npm test` (same lane) | every push | +| 3. Web e2e | A REAL dsh web composition booted in a throwaway `DSH_HOME` with the packed market installed. Two specs: the UI journey through real Chromium (console tripwire fails the run on any page error), and the **real install chain** — install route → registry check → real pnpm resolution → validation → patch layer → cordis hot mount | `tests/web/*.e2e.ts` + `tests/web/scaffold.ts` | `npm run test:web` | own job | +| 4. Perimeter | Real pnpm 9/10/11 behavior matrix (pins the failure signatures behind #20/#21/#22) and the packaging/restart smoke scripts | `tests/*.compat.spec.ts`, `scripts/*.mjs` | `npm run test:compat`, `npm run check` | own job / in check | + +## Why layer 3 carries the install chain + +Layer 1 drives the same chain against `FakeDsh`, a simulator written in this +repo. That can only show the code agrees with **our model** of pnpm and +cordis — and every install bug that actually shipped (#103, #122, #135, #147) +was a place where the model itself was wrong. More layer-1 specs cannot +close that gap; only real machinery can. + +So `install.e2e.ts` uses the real pnpm, the real loader and the real patch +layer, and installs a fixture plugin that **proves its own liveness**: the +fixture registers an HTTP route from inside `apply()`, so the route can only +answer if cordis genuinely resolved the package, loaded the module and ran +it. The market's `activation[name].state` is an inference and is asserted +*against* that ground truth rather than trusted as one. + +Fixtures live in `tests/web/fixtures/` and are served to pnpm by a local npm +registry (`tests/web/registry.ts`) — a real packument plus tarball over +localhost, so resolution is the ordinary code path with nothing published and +no network involved. Unknown packages redirect upstream so pnpm can still +replay the rest of the tree. Two traps worth knowing: + +- `npm_config_registry` **outranks `.npmrc`**, and `npm run` puts the + caller's registry there — the scaffold sets both. +- The registry server shares the test process, so anything that blocks the + event loop (`execSync`) starves it and pnpm times out. + +## Running layer 3 locally + +The scaffold needs a dsh CLI. Either have `dsh` on PATH, or point it at a +source checkout: + +```sh +DSHM_E2E_DSH="node --import tsx/esm /path/to/deepseek-harness/apps/cli/src/bin.ts" \ +DSHM_E2E_DSH_CWD=/path/to/deepseek-harness \ +npm run test:web +``` + +Without a reachable dsh the e2e specs skip (they never fail a machine that +cannot run them). CI sets `DSHM_E2E_REQUIRED=1`, which turns that skip into a +hard failure — the job installs the CLI itself, so a silent skip there would +report "e2e passed" for a run that asserted nothing. Browser download can use a mirror: +`PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright npx playwright install chromium`. + +## The one check no lane can run: provisioning pnpm + +Every automated lane has pnpm already — CI installs it, developer machines +have it — so `provisionPnpm` (the "one small component is needed" banner and +its one-click fix) is only ever exercised against mocks. It is also the +subject of a standing cluster of reports (#105, #108, #132), which makes the +gap worth naming rather than papering over. + +Automating it would mean installing pnpm to set the profile up and then +removing it again, on a runner, before every run — fragile enough that it +would fail for its own reasons more often than for the product's. So this +one is a deliberate manual check, to repeat whenever `provisionPnpm` +changes: + +1. Build a profile with the market installed, WHILE pnpm is available + (`npm pack` + `dsh plugin --profile web add `). +2. Make pnpm genuinely unreachable. Dropping it from PATH is not enough: + `spawnEnv` deliberately re-adds `/opt/homebrew/bin`, `/usr/local/bin` and + `~/.local/bin`, because a GUI-launched host (DSH Desktop) starts with a + PATH that has none of them. Move the binary aside instead, and put it + back afterwards. +3. Point `npm_config_prefix` at a throwaway directory so the install under + test cannot touch the real toolchain. +4. Boot `dsh --profile web`, confirm `/dsh-market/status` reports + `pnpm: false`, then POST `/dsh-market/setup-pnpm`. + +What to assert, and why each one: + +- `pnpm: false` before — otherwise the banner never appears and the rest + proves nothing. +- `ok: true` from the route, and `pnpm: true` after. +- **pnpm really landed in the throwaway prefix.** A pass can otherwise come + from the probe finding a leftover elsewhere; check the directory. +- The throwaway prefix's `bin` is NOT on PATH. That is the point: the probe + can only succeed through the `npm prefix -g` lookup (#149), so this is the + only check that covers it for real. + +Last run: 2026-08-17 on macOS 15 (arm64), no pnpm and no corepack — +`pnpm: false` → one click → `ok: true` in 1.0 s → 39 MB of pnpm in the +throwaway prefix → `pnpm: true`. The corepack branch was not exercised (it +was absent); a machine that has corepack takes that path first. + +## Both dsh versions, before shipping a host-facing change + +The settings-card work assumed backward compatibility from reading the API +contract — installSettingsSection rides its own scoped fiber, the browser +half hides behind a nested inject — and reasoning is not evidence when the +failure mode is "the market's page disappears for everyone still on rc.6", +which is most reporters today. + +Run the layer-3 lane against both: + +```sh +npm install -g @deepseek-ai/dsh@0.1.0-rc.6 && npm run test:web +npm install -g @deepseek-ai/dsh@0.1.0-rc.7 && npm run test:web +``` + +Result on 2026-08-17, both green (18 specs). It also corrected the reasoning +behind the guard: rc.6 already HAS a settings service and serves the +market's namespace fine — what rc.7 opened is the browser-side allowlist +deciding whose card may render. So the Host half was never the compatibility +risk; the client half was, and the nested inject is what covers it. + +## Conventions + +- **Red first**: a bug fix lands with the failing test that reproduces it. + Every issue number in a test title is a reproduced-then-fixed incident. +- **Mutation-audited**: the suites have been checked with targeted mutations + (every mutation must kill ≥1 test; every test must be killable). Keep new + tests killable — no assertion that can be satisfied by a fallback path. +- **Fake pnpm never invents behavior**: everything FakeDsh simulates is a + signature pinned by the real-pnpm compat lane first. diff --git a/packages/dshmarket/UPDATE-API-V1.md b/packages/dshmarket/UPDATE-API-V1.md new file mode 100644 index 000000000..5543e1648 --- /dev/null +++ b/packages/dshmarket/UPDATE-API-V1.md @@ -0,0 +1,132 @@ +# Public plugin update API v1 + +> **Status: beta.** The shape described here may still change between +> releases. `GET /dsh-market/api/v1/capabilities` reports +> `"stability": "beta"` while that is true, and `"stable"` once it stops +> moving — read that field rather than assuming from the `v1` in the path. +> +> Nothing here is going away; what is not yet promised is that field names +> and response shapes will survive untouched. If you ship against it now, +> say so in an issue: a shape somebody depends on is a much stronger reason +> not to move it, and it is how this reaches `stable`. + + +`dshmarket` exposes a small, versioned, same-origin JSON API for plugin-owned +update surfaces. It lets a plugin show its own update button without spawning a +package manager, copying the Market installation algorithm, or binding to the +Market UI's private response fields. + +All responses carry: + +```json +{ "schema": "dsh-market/update-api/v1" } +``` + +Clients must discover the API before enabling mutation controls: + +```http +GET /dsh-market/api/v1/capabilities +``` + +The response names the Market version, profile, runtime (`web` or `desktop`), +supported features, restart owner and endpoint paths. A client must hide its +restart button when `restart.supported` is false. Desktop and supervised hosts +normally delegate restart to their owning shell or operator. + +## Check one installed package + +```http +GET /dsh-market/api/v1/updates?name=dsh-mcp-connector&force=1 +``` + +The response includes the installed version, target version, source kind and +whether the target is a forward update. Omitting `force=1` allows the Market's +short update-check cache. + +## Start and observe an update + +Mutation requests require the same-origin protection used by the Market UI. + +```http +POST /dsh-market/api/v1/updates +Content-Type: application/json + +{ "packageName": "dsh-mcp-connector" } +``` + +An accepted request returns HTTP `202` immediately with an `operationId`. +Passing `"force": true` opts this one operation out of the registry release-age +wait; clients should offer it only after the normal operation reports +`RELEASE_TOO_FRESH` or `VERSION_UNCHANGED`. + +Poll the operation by id: + +```http +GET /dsh-market/api/v1/operations?operationId= +``` + +States are `queued`, `running`, `succeeded`, `failed`, `cancelled` and +`rolled-back`. Running operations include structured package progress when +pnpm provides it. Terminal operations include: + +- the before and actually installed versions; +- `refreshRequired` and `restartRequired` outcomes; +- a stable failure code, bounded user-facing message and retryability; +- whether a compatibility rollback is currently available. + +For npm updates, Market verifies the package version that pnpm actually placed +on disk before reporting success. This prevents pnpm's release-age policy or a +lagging registry mirror from silently turning `@latest` into an older or +unexpected build. A mismatch is restored from the pre-update recovery point +and reported as one of these stable failures: + +- `DOWNGRADE_DETECTED`: the resolved version is older than the version present + before the operation; not retryable without a new target. +- `RESOLVED_VERSION_MISMATCH`: the resolved version is BELOW the registry + target checked immediately before installation; retryable after registry or + mirror convergence. A version above that target is accepted — `latest` can + move forward while the install is still running. + +Provider clients should still compare `beforeVersion`, the target they showed +to the user, and `installedVersion` before offering restart. That independent +check protects clients connected to another compatible provider implementation. + +Up to 50 operation records live in the current Host process. A boot id is +embedded in every operation id, so a client never mistakes a stale browser +record for a task belonging to the replacement process. + +## Roll back + +```http +POST /dsh-market/api/v1/rollback +Content-Type: application/json + +{ "operationId": "" } +``` + +Rollback is intentionally capability- and operation-scoped. It is available +only when the Market's compatibility verification retained a recovery point; +a later mutation may supersede it. The normalized result is written back to +the same operation record. + +## Restart + +```http +POST /dsh-market/api/v1/restart +Content-Type: application/json + +{} +``` + +This preserves the Market's stricter restart guard: direct loopback, +same-origin, no forwarding headers, no package mutation in progress, and a Host +whose lifecycle is not owned by Desktop or a supervisor. Clients must feature +detect it; they must not invent an alternative process-control path. + +## Compatibility policy + +- New optional response fields may be added within v1. +- Existing v1 fields and meanings are not repurposed. +- A breaking change uses a new path and schema version. +- When discovery is unavailable, plugin UIs should fall back to opening the + Market rather than calling legacy `/dsh-market/*` mutation routes directly. diff --git a/packages/dshmarket/assets/demo-en.png b/packages/dshmarket/assets/demo-en.png new file mode 100644 index 000000000..32b2d45b1 Binary files /dev/null and b/packages/dshmarket/assets/demo-en.png differ diff --git a/packages/dshmarket/assets/demo-zh.png b/packages/dshmarket/assets/demo-zh.png new file mode 100644 index 000000000..252ebbba1 Binary files /dev/null and b/packages/dshmarket/assets/demo-zh.png differ diff --git a/packages/dshmarket/assets/logo-512.png b/packages/dshmarket/assets/logo-512.png new file mode 100644 index 000000000..8aed9ad33 Binary files /dev/null and b/packages/dshmarket/assets/logo-512.png differ diff --git a/packages/dshmarket/assets/logo-mono.svg b/packages/dshmarket/assets/logo-mono.svg new file mode 100644 index 000000000..0ce845d03 --- /dev/null +++ b/packages/dshmarket/assets/logo-mono.svg @@ -0,0 +1,14 @@ + + dsh-market + + + + + + + + + + + + diff --git a/packages/dshmarket/assets/logo.svg b/packages/dshmarket/assets/logo.svg new file mode 100644 index 000000000..6aeaefc07 --- /dev/null +++ b/packages/dshmarket/assets/logo.svg @@ -0,0 +1,19 @@ + + dsh-market + + + + + + + + + + + + + + + + diff --git a/packages/dshmarket/assets/themes-en.png b/packages/dshmarket/assets/themes-en.png new file mode 100644 index 000000000..d44607587 Binary files /dev/null and b/packages/dshmarket/assets/themes-en.png differ diff --git a/packages/dshmarket/assets/themes-zh.png b/packages/dshmarket/assets/themes-zh.png new file mode 100644 index 000000000..8e5c6b11f Binary files /dev/null and b/packages/dshmarket/assets/themes-zh.png differ diff --git a/packages/dshmarket/assets/wordmark-dark.svg b/packages/dshmarket/assets/wordmark-dark.svg new file mode 100644 index 000000000..036f3862c --- /dev/null +++ b/packages/dshmarket/assets/wordmark-dark.svg @@ -0,0 +1,17 @@ + + dsh-market + + + + + + + + + + + + + + dsh-market + diff --git a/packages/dshmarket/assets/wordmark.svg b/packages/dshmarket/assets/wordmark.svg new file mode 100644 index 000000000..18763debb --- /dev/null +++ b/packages/dshmarket/assets/wordmark.svg @@ -0,0 +1,17 @@ + + dsh-market + + + + + + + + + + + + + + dsh-market + diff --git a/packages/dshmarket/client/client.js b/packages/dshmarket/client/client.js new file mode 100644 index 000000000..cf618c3ae --- /dev/null +++ b/packages/dshmarket/client/client.js @@ -0,0 +1,9959 @@ +window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => { + + + var module = { exports: {} }; + var exports = module.exports; + Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); + //#region \0rolldown/runtime.js + var __create = Object.create; + var __defProp = Object.defineProperty; + var __getOwnPropDesc = Object.getOwnPropertyDescriptor; + var __getOwnPropNames = Object.getOwnPropertyNames; + var __getProtoOf = Object.getPrototypeOf; + var __hasOwnProp = Object.prototype.hasOwnProperty; + var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { + key = keys[i]; + if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { + get: ((k) => from[k]).bind(null, key), + enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable + }); + } + return to; + }; + var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { + value: mod, + enumerable: true + }) : target, mod)); + //#endregion + let react = require("react"); + let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives"); + _deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1); + let react_jsx_runtime = require("react/jsx-runtime"); + let react_dom = require("react-dom"); + //#region src/client/locales.ts + /** zh/en dictionaries for the Market settings section and install toast. */ + const zh = { + nav: "插件市场", + setCardDesc: "查看插件市场版本与设置。", + setSelfUpToDate: "已是最新版本", + setSelfUpdateReady: "有新版本", + setSelfUpdateHint: "更新会下载新版本,重启后生效。", + setSelfUpToDateHint: "", + setSelfUpdate: "更新", + setSelfUpdatedHint: "已下载完成。重启 DeepSeek Harness 后新版本才会生效——前端页面会立即更新,服务端不会。", + setRegion: "下载区域", + setRegionGlobal: "全球", + setRegionChina: "中国大陆", + setRegionGlobalHint: "从 npm 和 GitHub 官方源下载。", + setRegionChinaHint: "中国大陆访问更快。仅改变下载线路,插件来源不变。", + setRegionAuto: "已根据网络状况自动选择,可随时切换。", + setChannel: "更新通道", + setChannelStable: "稳定版", + setChannelBeta: "Beta", + setChannelStableHint: "仅接收正式发布版本。", + setChannelBetaHint: "提前获取待验证版本,可能不稳定。仅影响插件市场自身。", + setChannelDev: "开发版", + setChannelDevHint: "开发分支构建,未经验证,不建议普通用户使用。", + setChannelSwitch: "切换至", + setChannelSwitchHint: "该版本低于当前已安装版本。", + setSelfRemove: "卸载插件市场", + setSelfRemoveHint: "从这个 profile 卸载市场。已装的其它插件不受影响。", + setSelfConfirm: "确定要卸载插件市场吗?卸载后需使用命令行重新安装。", + setSelfPurge: "同时清除市场自己的数据", + setSelfPurgeOn: "将删除:市场的停用清单与自定义分组,以及市场写进 profile 补丁文件的停用行——被市场停用的插件将恢复运行。不涉及密码或令牌,市场从不把它们存在磁盘上。", + setSelfPurgeOff: "保留市场数据。注意:被市场停用的插件会保持停用,且移除后将没有界面可将其重新启用。", + setSelfRemoveConfirm: "确认卸载", + setSelfCancel: "取消", + setSelfWorking: "正在卸载…", + setSelfRemoved: "已卸载", + setSelfRemovedHint: "重启 DeepSeek Harness 后完全清理。", + setSelfFailed: "操作失败", + versionHint: "插件市场版本 — 反馈问题时请附上", + subtitle: "发现社区为 DeepSeek Harness 开发的插件", + submitPlugin: "申请收录插件 ↗", + descExpand: "展开", + descCollapse: "收起", + searchPh: "搜索插件,例如:通知、终端、记忆…", + tabDiscover: "发现", + tabInstalled: "已安装", + tabAdvanced: "高级", + all: "全部", + install: "安装", + installing: "安装中…", + installedBadge: "✓ 已安装", + alreadyInstalled: "✓ 已安装", + restartBanner: "项变更完成,重启 DeepSeek Harness 后生效", + uninstall: "卸载", + confirmRemove: "确认卸载?", + uninstalling: "卸载中…", + uninstallConfirmDesc: "将从当前 profile 中移除该插件。", + restartHint: "重启方式:关闭当前 dsh 进程后重新运行(例如 dsh web)", + restartHintSupervised: "本进程是 {0} 服务的主进程,重启交给它负责——市场自己重启会连带杀掉 cgroup 里的接管进程,服务将起不来。请执行 systemctl restart <你的 unit>。确认你的配置能承受市场自行重启(如 KillMode=process),可在本插件配置里手动打开「允许重启」。", + restartHintDebugged: "当前 dsh 进程正在被调试器附着。请从 IDE 或终端停止该进程后重新启动,市场不能从 UI 里杀掉正在调试的宿主。", + confirmTitle: "安装", + confirmWarn: "插件是社区第三方代码。安装即表示你信任该来源;构建脚本默认被禁止执行。", + cancel: "取消", + empty: "没有匹配的插件", + installedEmpty: "尚未安装社区插件,可前往「发现」页浏览", + loadFail: "插件目录加载失败,请稍后重试", + loadRetry: "重试", + installFail: "安装失败", + viewSource: "源码", + refreshBanner: "项变更需刷新页面生效", + refresh: "刷新页面", + update: "更新", + updating: "更新中…", + updated: "✓ 已更新,重启后生效", + cancelOp: "取消", + cancelled: "已取消", + busyWait: "已有操作正在进行,请等待其完成(同一时间只执行一个安装/更新/卸载)", + agentBusyUpdate: "有 agent 正在运行,请等待其完成或将其取消后再更新——更新会直接替换插件文件,运行中的 agent 可能中途报错或新旧版本混用。", + agentBusyInstall: "有 agent 正在运行,请等待其完成或将其取消后再安装——安装会修改插件文件,运行中的 agent 可能中途报错。", + compatRiskBanner: "检测到兼容性风险,建议重启前到诊断页处理,或一键回滚本次操作。", + compatRiskBannerNoRollback: "检测到兼容性风险,建议重启前到诊断页处理。", + shadowNameBanner: "本次操作让同一个插件名在两个层里同时存在,重启后只有一个会生效:", + brokenBundleBanner: "下列插件的前端文件已损坏、无法解析,刷新后界面可能空白,建议回滚:", + goDiagnose: "前往诊断页修复", + rollbackNow: "回滚本次操作", + rollingBack: "回滚中…", + rollbackUnavailable: "无法确认更新前的准确来源,自动回滚不可用。", + approveBuilds: "放行构建脚本并重试", + buildsSkipped: "该插件需要运行构建脚本才能工作,出于安全考虑已默认阻止。确认来源可信后,可放行并重新安装:", + restartNow: "立即重启", + dismiss: "关闭提示", + dismissNotice: "关闭此提示", + restarting: "正在重启…", + restartFail: "重启失败", + restartTimeout: "等待 DeepSeek Harness 启动超时", + updateNow: "立即更新", + updateFail: "更新失败", + upToDate: "已是最新", + linkedDev: "本地开发", + notesLink: "更新内容", + notesRelease: "版本说明", + notesCommits: "提交记录", + notesCommitsRecent: "最近提交(未能对齐你当前安装的版本,仅供参考)", + notesNpm: "版本发布时间", + notesNone: "该插件未提供更新说明。", + notesLoadFail: "暂时读不到更新说明,稍后再试。", + restore: "恢复", + restoreOnline: "换用线上版本", + restoreHint: "会卸载本地版本,重新安装线上版本,并保持更新检测。无法回退,请二次确认。", + restoreContinue: "继续更新", + restoreNoCatalog: "精选目录里没有这个插件,无法恢复到线上版本。可以卸载本地版,或继续用本地开发。", + restoreFail: "恢复失败", + exportLog: "导出日志", + exportingLog: "导出中…", + exportedLog: "日志已导出:dsh-market-log.txt,请将其附在 issue 中", + exportLogFail: "日志导出失败", + readme: "使用说明", + comments: "评论", + commentsTitle: "评论", + commentsNote: "评论来自 GitHub Discussions。打开后会连接 giscus.app 与 GitHub;发表评论需要 GitHub 账号。", + commentsLoading: "正在加载评论…", + commentsError: "评论加载失败。请检查网络连接后重试。", + commentsRetry: "重试", + commentsOnGitHub: "前往 GitHub 查看讨论", + terminalWarn: "这看起来是终端/命令行插件:安装到网页版 profile 可能不会生效,甚至导致 DeepSeek Harness 无法启动。建议先阅读其使用说明,并按说明安装到对应的 profile。", + conflictTitle: "无法安装:与已安装的插件冲突", + conflictBody: "和已安装的插件冲突,只能留一个:", + conflictReverted: "已自动撤销,未改动任何插件", + conflictKeep: "保留已安装的插件", + conflictKeepNote: "", + conflictSwap: "改为安装这个", + conflictSwapNote: "被卸载的插件配置会保留,可以再装回来", + conflictDetails: "详情", + conflictOutcomeInstall: "安装", + conflictOutcomeSkip: "不安装", + conflictOutcomeKeep: "保留", + conflictOutcomeRemove: "卸载", + conflictWhy: "cordis 不接受包含重复条目 id 的插件树,保留该插件将导致 DeepSeek Harness 下次启动失败。", + opTitle: "任务", + opInstalling: "正在安装", + opNeedsYou: "项需处理", + opQueued: "排队中", + opQueuedAhead: "前面还有", + opRunning: "正在处理", + opNeedsChoice: "无法安装 · 已自动撤销,未改动任何插件", + opDone: "已完成", + opDoneRefresh: "已安装 · 刷新页面后生效", + opLeaveHint: "可离开本页面,完成后将通知你", + opEmpty: "暂无进行中的操作", + opEmptyHint: "安装、更新与卸载的进度将显示在这里", + opClose: "收起", + opClear: "清空已完成", + opDequeue: "移出队列", + opRetry: "重试", + opKind_install: "安装", + opKind_update: "更新", + opKind_uninstall: "卸载", + opBlockedCard: "无法安装 · 查看详情", + conflictDeclined: "已选择保留现有插件,未安装", + conflictReplacing: "正在替换…", + conflictReplaceFailed: "替换未完成。下列插件已卸载且未自动恢复,请手动确认:", + lightboxClose: "关闭预览", + lightboxPrev: "上一张", + lightboxNext: "下一张", + envMissing: "安装插件前需要先配置 pnpm 环境", + envFix: "自动配置", + envFixing: "正在配置…", + envFixFail: "自动配置失败。请使用「导出日志」,并将文件附在 issue 中", + loading: "正在加载插件目录…", + backTop: "回到顶部", + confirm: "确认", + confirmInstall: "确认安装", + cmdDetails: "安装命令", + catsMore: "更多分类", + catsLess: "收起", + filter: "筛选", + filterSort: "排序字段", + filterDir: "排序方向", + filterTime: "发布时间范围", + sortDownloads: "npm 下载量(近 30 天)", + sortStars: "Star 数", + sortAdded: "发布时间", + sortDesc: "降序", + sortAsc: "升序", + sortNewest: "最新", + sortOldest: "最旧", + timeAll: "全部时间", + timeDay: "最近 1 天", + timeWeek: "最近 7 天", + timeMonth: "最近 30 天", + timeQuarter: "最近 90 天", + timeYear: "最近 1 年", + published: "发布于", + prevPage: "上一页", + nextPage: "下一页", + firstPage: "首页", + lastPage: "末页", + pageInfo: "第 {0} / {1} 页", + perPage: "每页", + marketUpdate: "更新插件市场", + updateAll: "全部更新", + ignoreUpdateNotice: "本次不再提醒", + ignoreAllUpdateNotices: "本次全部忽略", + updateNoticeIgnored: "本次已忽略提醒", + tabThemes: "主题", + tabBackup: "备份与恢复", + backupLocal: "本地文件", + backupDownload: "导出备份", + backupImport: "导入并预览", + backupHint: "仅包含插件清单和 profile 配置,不包含 node_modules;恢复会按清单重新安装插件。", + webdav: "WebDAV", + webdavPreset: "服务商预设", + webdavUrl: "备份文件 URL", + webdavUser: "用户名(可选)", + webdavPassword: "密码(可选)", + webdavUpload: "上传备份", + webdavRestore: "从 WebDAV 恢复", + autoBackup: "每天自动备份(打开市场时)", + webdavNote: "WebDAV 地址与用户名仅保存在当前浏览器;密码只存于服务端,每次会话需重新输入。", + localOnly: "WebDAV 地址和凭证仅保存在当前浏览器。", + credsWarning: "请注意:备份包含 profile 配置,以及可能含密钥的文件(config.toml、.env 等)。本地导出的文件会原样包含这些内容,上传 WebDAV 前请确认目标可信。", + gist: "GitHub Gist", + gistToken: "GitHub token(仅本次会话内存,刷新后需重新输入;留空则使用服务端 DSH_GITHUB_TOKEN 或已登录的 gh CLI)", + gistId: "Gist ID 或链接(导出留空则新建私有 Gist)", + gistVerify: "验证连接", + gistExport: "导出到 Gist…", + gistImport: "从 Gist 导入", + gistExportDone: "已导出到 Gist", + gistVerifySource: "验证通过(Token 来源:{0})", + gistSrcToken: "手动输入", + gistSrcEnv: "环境变量 DSH_GITHUB_TOKEN", + gistSrcGh: "gh CLI", + gistExportSelect: "选择要导出的插件", + gistExportHint: "全部勾选 = 完整备份(含配置文件);取消勾选 = 仅导出所选插件,可另行勾选「包含配置文件」。", + gistSelectAll: "全选", + gistSelectNone: "全不选", + gistIncludeConfig: "包含配置文件(可能含密钥,请确认)", + gistNoPlugins: "当前 profile 没有可导出的插件", + gistSpecLocal: "本地", + gistExportGo: "导出", + gistCreated: "已导出:", + gistErrTimeout: "GitHub 请求超时,请检查网络后重试", + gistErrNetwork: "无法连接 GitHub,请检查网络或代理设置", + gistErrAuth: "GitHub 认证失败:Token 无效、已过期,或 gh CLI 未登录", + gistErrNotFound: "未找到该 Gist,请检查 ID 或链接", + gistErrRateLimit: "GitHub 限流或 Token 权限不足", + gistErrInvalid: "Gist 内容无效或备份格式不受支持", + gistModeUpdate: "更新到输入框中的 Gist", + gistModeCreate: "新建一个私有 Gist", + gistErrNoId: "更新模式需要 Gist ID:请填写,或切换为「新建」", + gistNote: "与本地导出(本机存档)不同,Gist 用于跨机器同步:导出到私有 Gist 后,可在其他电脑用同一账号导入复用。Token 仅保存在本次会话内存中,绝不写入浏览器存储;已登录 gh CLI 或设置 DSH_GITHUB_TOKEN 则无需输入。Gist 为私有,单文件上限 1 MB。", + backupWorking: "处理中…", + backupDone: "备份已上传", + restoreDone: "恢复完成,请重启 DeepSeek Harness", + restorePartial: "恢复已完成,但下列插件安装失败:", + restoreUnportable: "依赖指向另一台机器上的绝对路径,本机不存在。请手动改为本机路径,或重新安装该插件", + restoreBootError: "恢复后仍无法启动:", + orphanBundle: "⚠️ 下次启动会失败:profile 声明了这些 bundle,但它们没有安装。请重新安装,或从 profile 的 package.json 的 dsh.profile.bundles 中删除:", + restoreConfirm: "恢复将覆盖当前 profile 配置并重新安装插件,确定继续吗?", + restorePreviewDone: "备份已导入,请在「已安装」中确认后开始恢复", + restoreMissing: "备份中有 {0} 个插件尚未安装", + restoreStart: "开始恢复", + notInstalled: "未安装", + themeApply: "使用", + themeActive: "使用中", + themeDeactivate: "停用", + themeEmpty: "目录中暂无主题", + themePreview: "预览", + themePreviewLoading: "正在查找预览…", + themePreviewMissing: "暂无预览", + themePreviewCount: "{0} 张预览", + themeResultCount: "{0} 款主题", + themeFullscreen: "全屏浏览", + themeExitFullscreen: "退出全屏", + progressHint: "首次安装需要下载与解析依赖,大型插件可能需要 1-3 分钟", + toastReady: "已安装并已生效", + toastTheme: "已启用。可在 设置 → 插件市场 → 主题 中随时切换", + toastToggledOn: "已启用", + toastToggledOff: "已停用,相关功能立即停止", + gotIt: "关闭", + stateLive: "已生效", + stateRestart: "已安装,重启后生效", + stateInert: "已安装,未生效", + stateBroken: "已安装,校验未通过", + stateDisabled: "已停用", + phaseResolving: "解析依赖", + phaseDownloading: "下载中", + phaseLinking: "链接依赖", + phaseBuilding: "运行构建脚本", + cancelling: "正在取消…", + packagesDone: "已处理 {0} 个包", + updatedLive: "✓ 已更新,已生效", + partialNote: "已取消,部分变更已写入", + reconciledNote: "已卸载,但 pnpm 在收尾阶段报错(通常是文件被占用);配置已同步,无需重试", + actWhy: "为什么未生效?", + tabList: "列表", + tabGroups: "分组", + repoLink: "在 GitHub 上查看仓库", + disabledState: "已停用", + noteAdd: "添加备注", + noteEdit: "编辑备注", + noteTheirs: "原文", + noteMine: "我的备注", + noteSave: "保存", + notePlaceholder: "用你自己的话描述这个插件,将代替作者简介显示", + noteSeeTheirs: "查看作者写的简介", + noteSeeMine: "返回我的备注", + switchOnLabel: "启用中", + enable: "启用", + disable: "停用", + toggleFail: "切换失败", + deprecatedBadge: "已废弃", + deprecatedWarn: "该插件已被目录标记为废弃,不建议新用户安装。", + viewReplacement: "查看替代品", + installReplacement: "安装替代品", + replacementHint: "目录建议改用", + groupNew: "新建分组", + groupNamePh: "分组名称", + groupRename: "重命名", + groupDelete: "删除分组", + groupConfirmDelete: "确认删除?", + ungrouped: "未分组", + groupAssign: "分配", + groupRemove: "移出", + groupEmpty: "该组暂无成员", + groupMixed: "部分启用", + noGroups: "尚无分组,请先新建", + groupCreate: "创建", + groupAdd: "加入插件", + groupAddTheme: "加入主题", + groupAddEmpty: "所有已安装插件都已在该组中", + tabDiagnostics: "诊断", + checkIssues: "发现问题", + checkErrors: "错误", + checkWarnings: "警告", + checkLoading: "正在分析 profile…", + checkLoadFail: "诊断加载失败:", + checkProfile: "profile", + checkErrorsEmpty: "没有错误", + checkWarningsEmpty: "没有警告", + checkBundles: "插件加载顺序(bundle)", + checkBundlesEmpty: "未声明任何插件层", + checkOfficial: "官方", + checkCommunity: "社区", + checkSource: "来源", + checkEntries: "加载条目(loader id)", + checkPatch: "patch 文件", + checkDir: "目录", + checkDuplicates: "重复的插件条目", + checkDuplicatesEmpty: "没有重复的插件条目", + checkHoisted: "顶层", + checkPeerMismatches: "依赖版本不匹配", + checkPeerEmpty: "没有依赖版本不匹配", + checkPeerInfo: "另有 {0} 条信息(未确认问题)", + checkPeerOverview: "{0} 不匹配 · {1} 条信息", + checkRange: "声明范围", + checkResolved: "实际版本", + checkSatisfied: "满足", + checkUnsatisfied: "不满足", + checkUnknown: "未知", + checkPeerRisk: "依赖风险", + checkPeerRiskEmpty: "没有风险级依赖不匹配", + checkPeerWarning: "依赖警告", + checkPeerWarningEmpty: "没有警告级依赖不匹配", + checkPeerInfoTier: "依赖信息", + checkPeerInfoTierEmpty: "没有信息级依赖条目", + checkPeerSatisfied: "已满足的依赖", + checkPeerSatisfiedEmpty: "没有已满足的依赖条目", + peerRiskBelowMin: "低于声明下界", + peerRiskAboveMax: "高于显式上界", + peerWarnOptional: "可选依赖", + peerWarnAboveMax: "超出声明上界", + peerInfoUnverified: "未达判定", + checkMultiVersion: "核心包多版本", + checkMultiEmpty: "没有多版本核心包", + checkOverrides: "覆盖关系", + checkOverridesEmpty: "没有覆盖关系", + checkOverridden: "覆盖了", + checkOrphans: "无效的配置条目", + checkOrphansEmpty: "没有无效配置条目", + orphanInsertNotArray: "格式错误", + orphanInsertTargetMissing: "目标不存在", + orphanInsertTargetNotGroup: "目标不是分组", + orphanIdRequired: "缺少标识", + orphanPatchTargetMissing: "目标不存在", + orphanNameMismatch: "名称不匹配", + orphanReasonOther: "其他", + diagExplain: "术语说明", + diagExplainText: "本页检查插件之间的冲突、依赖版本不匹配与加载顺序问题。常用术语:", + diagTermBundle: "bundle = 一个插件包,会按顺序叠加加载", + diagTermEntry: "加载条目 = 插件在运行组合里注册的每一项", + diagTermPeer: "对等依赖 = 插件要求宿主环境提供的另一个包", + diagTermShadow: "遮蔽 = 插件自带的旧版包盖住了宿主的新版", + diagTermOrphan: "无效条目 = 配置里引用了不存在的东西", + diagTermOrder: "加载顺序 = 插件被激活的先后,后加载者可能覆盖先加载者", + orderSection: "排序", + orderUp: "↑", + orderDown: "↓", + orderApply: "应用顺序", + orderDrag: "拖拽排序", + orderDragHint: "拖动 ⠿ 调整顺序,选择「应用顺序」后才会保存", + orderConflicts: "当前顺序的 before/after 冲突", + orderApplied: "✓ 已应用,重启后生效", + orderSuggestApply: "采用建议顺序", + orderSuggestHint: "建议顺序(按 before/after 规则自动排序):", + orderAutoSort: "自动排序", + orderAlreadyOptimal: "当前顺序已满足全部规则与插件依赖,无需调整", + orderTrialFail: "静态组合校验未通过:{0}", + orderDiffHint: "该顺序会改变组合:{0} 处覆盖、{1} 个无效条目、{2} 个重复条目", + duplicateNames: "同名插件(仅信息展示,不视为冲突)", + orderReset: "重置草稿", + checkRefresh: "重新检查", + aiFix: "AI 修复", + aiFixHint: "将诊断问题交由新对话中的 Agent 修复(内容已复制到剪贴板,由你决定是否发送)", + aiFixIntro: "请帮我修复 DeepSeek Harness 的插件问题(profile:{0})。诊断发现如下:", + aiFixDetect: "【第一步·先判断身份】开始前先判断:当前 Agent 所运行的进程,是否就是这个 profile 对应的 DeepSeek Harness 本机实例(即:诊断出的这套组合,就是你现在运行中的组合)?判断依据:当前工作目录 / $DSH_HOME / 进程命令行是否指向该 profile,或你是否运行在 deepseek-harness 进程内。若无法确定,一律按【是你】处理(更安全)。", + aiFixIfSelf: "【是 → 你就是这个 harness】严禁原地改动组合、harness 或核心包:不修改 cordis.patch.yml、不调整 bundle 顺序或启停、不安装/卸载/升级插件、不重新安装依赖、不升级或重启 dsh。三步:① 先只读分析,不改任何东西;② 给出 apply.sh/.bat(本次修复的改动)与 rollback.sh/.bat(完整撤销 apply);两者都幂等——apply 仅在无已有快照时才创建快照(否则不覆盖;已存在说明 apply 跑过,先回滚或确认再重跑),rollback 从这份快照恢复,因此 apply 跑到一半失败也能回滚、重复回滚也安全;③ 由我在外部终端执行 apply,并将 stdout/stderr 回贴给你判断。每步先说明理由、等我确认;绝不自行执行。", + aiFixScope: "【否 → 保守执行】你要是判定自己并非运行在目标 harness 内(独立进程),才可考虑:修改 dsh.profile.bundles 顺序、停用/启用插件、调整 cordis.patch.yml。注意:官方 bundle 不可移动;动手前先说明计划。", + aiFixConservative: "请保持保守:只修复上面列出的明确错误(启动失败/重复条目/风险级依赖不匹配)。警告和信息级问题(如可选依赖、未确认的依赖范围)仅在它们与明确错误相关时处理。不要做不必要的升级或重新排序;每项改动前说明理由并等待确认。", + aiFixCopied: "已复制修复提示词,请粘贴到新对话后发送", + aiFixFail: "无法访问剪贴板,请手动复制下面的诊断内容", + catConflict: "冲突", + catDeps: "依赖", + catRisk: "风险", + catWarn: "警告", + catInfo: "信息", + catOrder: "顺序", + /** Summary-strip tooltip for the order count (before/after rule conflicts). */ + checkOrderTip: "社区 bundle 加载顺序与插件声明的 before/after 规则冲突", + diagOkAll: "未发现冲突、依赖或加载顺序问题", + presetSection: "插件组合", + presetHint: "把当前的 bundle 顺序与停用列表保存为组合,随时切换;应用前会做试启动校验并自动创建快照。", + presetName: "组合名称", + presetSave: "保存当前状态", + presetSaving: "保存中…", + presetSaved: "组合已保存", + presetApply: "应用", + presetApplied: "已应用,重启后生效", + presetDelete: "删除", + presetDeleted: "组合已删除", + presetEmpty: "还没有保存过组合", + presetNameEmpty: "请输入组合名称", + presetFail: "组合操作失败:", + presetListFail: "组合列表加载失败:", + presetBundleCount: "{0} 个 bundle", + presetPreview: "预览", + presetPreviewTitle: "应用此组合将:", + presetReorder: "重排 {0} 个 bundle 的位置", + presetEnable: "启用 {0} 个插件", + presetDisable: "停用 {0} 个插件", + presetNoop: "无变更", + presetPreviewFail: "预览失败:", + snapSection: "快照与回滚", + snapHint: "应用变更前会自动创建快照;也可以手动创建快照,随时回滚到任意状态。", + snapCreate: "创建快照", + snapCreating: "创建中…", + snapCreated: "快照已创建", + snapEmpty: "还没有快照", + snapListFail: "快照列表加载失败:", + snapCreateFail: "创建快照失败:", + snapRestore: "回滚", + snapRestoring: "回滚中…", + snapRestored: "已回滚,重启后生效", + snapRestoreFail: "回滚失败:", + snapRestoreConfirm: "确认回滚", + snapRestoreConfirmText: "回滚会用快照覆盖当前 profile 的 package.json 与配置,且不可撤销(可先创建新快照再回滚)。", + snapFiles: "包含文件", + snapDelete: "删除", + snapDeleting: "删除中…", + snapDeleted: "快照已删除", + snapDeleteFail: "删除快照失败:", + snapDeleteConfirm: "确认删除", + snapDeleteConfirmText: "删除后该快照将无法恢复。", + marketNoToggle: "市场自身不能停用", + hostDependencyWarning: "插件在 dependencies 中声明了已知的 DSH 共享宿主包,可能遮蔽宿主版本(仅依据清单,未确认运行时重复):", + hostDependencyMore: "另有 {0} 条,已省略" + }; + const en = { + nav: "Plugin Market", + setCardDesc: "View the plugin market version and settings.", + setSelfUpToDate: "Up to date", + setSelfUpdateReady: "New version available:", + setSelfUpdateHint: "Updating downloads the new version; it takes effect after a restart.", + setSelfUpToDateHint: "", + setSelfUpdate: "Update", + setSelfUpdatedHint: "Downloaded. Restart DeepSeek Harness for it to take effect — the frontend updates at once, the server does not.", + setRegion: "Download region", + setRegionGlobal: "Global", + setRegionChina: "China mainland", + setRegionGlobalHint: "Downloads directly from npm and GitHub.", + setRegionChinaHint: "Faster in mainland China. Changes the download route, not the source.", + setRegionAuto: "Selected from a network check. Change it anytime.", + setChannel: "Update channel", + setChannelStable: "Stable", + setChannelBeta: "Beta", + setChannelStableHint: "Released versions only.", + setChannelBetaHint: "Early access to unverified versions. Affects the plugin market only.", + setChannelDev: "Dev", + setChannelDevHint: "Unverified builds from a development branch. Not recommended for general use.", + setChannelSwitch: "Switch to", + setChannelSwitchHint: "This version is lower than the one installed.", + setSelfRemove: "Uninstall the plugin market", + setSelfRemoveHint: "Uninstall the market from this profile. Your other plugins are untouched.", + setSelfConfirm: "Uninstall the plugin market? Reinstalling it requires the command line.", + setSelfPurge: "Also clear the market's own data", + setSelfPurgeOn: "Will delete: the market's disable list and custom groups, and the disable rows it wrote into the profile patch file — plugins the market switched off start running again. No passwords or tokens are involved; the market never stores those on disk.", + setSelfPurgeOff: "Keeps the market's data. Note: plugins the market switched off stay off, and once it is removed there is no UI left to switch them back on.", + setSelfRemoveConfirm: "Uninstall", + setSelfCancel: "Cancel", + setSelfWorking: "Uninstalling…", + setSelfRemoved: "Uninstalled", + setSelfRemovedHint: "Restart DeepSeek Harness to finish cleaning up.", + setSelfFailed: "The operation failed", + versionHint: "Plugin market version — include it when reporting an issue", + subtitle: "Discover community plugins for DeepSeek Harness", + submitPlugin: "Submit a plugin ↗", + descExpand: "Show more", + descCollapse: "Show less", + searchPh: "Search plugins: notify, terminal, memory…", + tabDiscover: "Discover", + tabInstalled: "Installed", + tabAdvanced: "Advanced", + all: "All", + install: "Install", + installing: "Installing…", + installedBadge: "✓ Installed", + alreadyInstalled: "✓ Installed", + restartBanner: "change(s) done — restart DeepSeek Harness to apply", + uninstall: "Uninstall", + confirmRemove: "Uninstall?", + uninstalling: "Uninstalling…", + uninstallConfirmDesc: "This removes the plugin from the current profile.", + restartHint: "To restart: stop the current dsh process and run it again (e.g. dsh web)", + restartHintSupervised: "This process is {0}'s own service process, so restarts belong to it — restarting from here would kill the takeover process along with the cgroup and the service would not come back. Use systemctl restart <your unit>. If your unit can survive a self-restart (KillMode=process), turn Allow restart on in this plugin's configuration.", + restartHintDebugged: "This dsh process is under a debugger. Stop it from your IDE or terminal and start it again — the market cannot kill a debug-attached host from the UI.", + confirmTitle: "Install", + confirmWarn: "Plugins are third-party community code. Installing means you trust this source; build scripts are blocked by default.", + cancel: "Cancel", + empty: "No plugins match", + installedEmpty: "No community plugins yet — browse the Discover tab", + loadFail: "The plugin catalog failed to load. Try again later.", + loadRetry: "Retry", + installFail: "Install failed", + viewSource: "Source", + refreshBanner: "change(s) applied — refresh the page to see them", + refresh: "Refresh", + update: "Update", + updating: "Updating…", + updated: "✓ Updated — restart to apply", + cancelOp: "Cancel", + cancelled: "Cancelled", + busyWait: "Another operation is already running — please wait for it to finish (one install/update/uninstall at a time)", + agentBusyUpdate: "An agent is currently working — wait for it to finish (or cancel it) before updating. Updates replace plugin files in place, so a working agent can fail or mix versions mid-turn.", + agentBusyInstall: "An agent is currently working — wait for it to finish (or cancel it) before installing. Installing changes plugin files, so a working agent can fail mid-turn.", + compatRiskBanner: "Compatibility risk detected — open Diagnostics before restarting, or roll this operation back.", + compatRiskBannerNoRollback: "Compatibility risk detected — open Diagnostics before restarting.", + shadowNameBanner: "This operation left one plugin name defined in two layers; only one will load after a restart:", + brokenBundleBanner: "These plugins now have a client bundle that will not parse; the page may come up blank after a refresh — rolling back is recommended:", + goDiagnose: "Open Diagnostics", + rollbackNow: "Roll back", + rollingBack: "Rolling back…", + rollbackUnavailable: "Automatic rollback is unavailable because the previous exact source could not be verified.", + approveBuilds: "Allow build scripts and retry", + buildsSkipped: "This plugin needs its build scripts to run; they are blocked by default for safety. If you trust the source, allow them and reinstall:", + restartNow: "Restart now", + dismiss: "Dismiss", + dismissNotice: "Dismiss this notice", + restarting: "Restarting…", + restartFail: "Restart failed", + restartTimeout: "Timed out waiting for DeepSeek Harness to start", + updateNow: "Update now", + updateFail: "Update failed", + upToDate: "Up to date", + linkedDev: "local", + notesLink: "What changed", + notesRelease: "Release notes", + notesCommits: "Commits", + notesCommitsRecent: "Recent commits (could not be aligned with your installed version — for reference only)", + notesNpm: "Version publish times", + notesNone: "This plugin does not publish update notes.", + notesLoadFail: "Update notes are unavailable right now — try again later.", + restore: "Restore", + restoreOnline: "Use online version", + restoreHint: "This will uninstall the local version, reinstall the catalog version, and keep update checks. This cannot be undone — please confirm again.", + restoreContinue: "Continue update", + restoreNoCatalog: "This plugin is not in the curated catalog, so it cannot be restored. Uninstall the local copy, or keep developing it locally.", + restoreFail: "Restore failed", + exportLog: "Export log", + exportingLog: "Exporting…", + exportedLog: "Log exported: dsh-market-log.txt — please attach it to your issue", + exportLogFail: "Log export failed", + readme: "README", + comments: "Comments", + commentsTitle: "Comments", + commentsNote: "Comments come from GitHub Discussions. Opening this connects to giscus.app and GitHub; posting needs a GitHub account.", + commentsLoading: "Loading comments…", + commentsError: "Comments could not be loaded. Check your connection and try again.", + commentsRetry: "Try again", + commentsOnGitHub: "Read the discussion on GitHub", + terminalWarn: "This looks like a terminal/CLI plugin: installing it into the web profile may do nothing, or even break DeepSeek Harness startup. Read its README and install it into the profile it targets.", + conflictTitle: "Cannot install: conflicts with an installed plugin", + conflictBody: "It conflicts with an installed plugin — only one can stay:", + conflictReverted: "Reverted automatically; nothing was changed", + conflictKeep: "Keep the installed plugin", + conflictKeepNote: "", + conflictSwap: "Switch to this one", + conflictSwapNote: "Settings of the uninstalled plugins are kept, so you can reinstall later", + conflictDetails: "Details", + conflictOutcomeInstall: "install", + conflictOutcomeSkip: "skip", + conflictOutcomeKeep: "keep", + conflictOutcomeRemove: "uninstall", + conflictWhy: "cordis refuses a plugin tree that contains duplicate entry ids; keeping this plugin would stop DeepSeek Harness from starting.", + opTitle: "Tasks", + opInstalling: "Installing", + opNeedsYou: "need attention", + opQueued: "Queued", + opQueuedAhead: "ahead:", + opRunning: "In progress", + opNeedsChoice: "Cannot install · reverted automatically, nothing changed", + opDone: "Done", + opDoneRefresh: "Installed · refresh the page to apply", + opLeaveHint: "You can leave this page; you will be notified when it finishes", + opEmpty: "No operations in progress", + opEmptyHint: "Install, update and uninstall progress appears here", + opClose: "Collapse", + opClear: "Clear finished", + opDequeue: "Remove from queue", + opRetry: "Retry", + opKind_install: "Install", + opKind_update: "Update", + opKind_uninstall: "Uninstall", + opBlockedCard: "Cannot install · details", + conflictDeclined: "You kept the installed plugins; this one was not installed", + conflictReplacing: "Replacing…", + conflictReplaceFailed: "Replace did not finish. The plugins below were uninstalled and not restored — please check them:", + lightboxClose: "Close preview", + lightboxPrev: "Previous", + lightboxNext: "Next", + envMissing: "pnpm needs to be set up before installing plugins", + envFix: "Set up automatically", + envFixing: "Setting up…", + envFixFail: "Automatic setup failed — export the log and attach it to your issue", + loading: "Loading the catalog…", + backTop: "Back to top", + confirm: "Confirm", + confirmInstall: "Confirm install", + cmdDetails: "Install command", + catsMore: "More", + catsLess: "Less", + filter: "Filter", + filterSort: "Sort field", + filterDir: "Order", + filterTime: "Released within", + sortDownloads: "npm downloads (30d)", + sortStars: "Stars", + sortAdded: "Release date", + sortDesc: "Descending", + sortAsc: "Ascending", + sortNewest: "Newest", + sortOldest: "Oldest", + timeAll: "Any time", + timeDay: "Last day", + timeWeek: "Last 7 days", + timeMonth: "Last 30 days", + timeQuarter: "Last 90 days", + timeYear: "Last year", + published: "released", + prevPage: "Previous", + nextPage: "Next", + firstPage: "First", + lastPage: "Last", + pageInfo: "Page {0} of {1}", + perPage: "Per page", + marketUpdate: "Update the plugin market", + updateAll: "Update all", + ignoreUpdateNotice: "Ignore for this boot", + ignoreAllUpdateNotices: "Ignore all for this boot", + updateNoticeIgnored: "Reminder ignored for this boot", + tabThemes: "Themes", + tabBackup: "Backup & Restore", + backupLocal: "Local file", + backupDownload: "Export backup", + backupImport: "Import and preview", + backupHint: "Includes the plugin list and profile configuration, never node_modules. Restore reinstalls plugins from the list.", + webdav: "WebDAV", + webdavPreset: "Provider preset", + webdavUrl: "Backup file URL", + webdavUser: "Username (optional)", + webdavPassword: "Password (optional)", + webdavUpload: "Upload backup", + webdavRestore: "Restore from WebDAV", + autoBackup: "Back up daily (when the market opens)", + webdavNote: "The WebDAV URL and username stay in this browser only; the password is kept server-side and must be re-entered each session.", + localOnly: "The WebDAV URL and credentials stay in this browser only.", + credsWarning: "Note: backups include profile configuration and files that may hold secrets (config.toml, .env, …). Local exports are unmodified, so upload only to a WebDAV target you trust.", + gist: "GitHub Gist", + gistToken: "GitHub token (session memory only — re-enter after refresh; leave empty to use server-side DSH_GITHUB_TOKEN or a logged-in gh CLI)", + gistId: "Gist ID or URL (leave empty on export to create a new private Gist)", + gistVerify: "Verify connection", + gistExport: "Export to Gist…", + gistImport: "Import from Gist", + gistExportDone: "Exported to Gist", + gistVerifySource: "Verified — token source: {0}", + gistSrcToken: "manually entered", + gistSrcEnv: "DSH_GITHUB_TOKEN env var", + gistSrcGh: "gh CLI", + gistExportSelect: "Select plugins to export", + gistExportHint: "All checked = full backup (config included); unchecking any exports only the selected plugins, with an optional “include config” flag.", + gistSelectAll: "Select all", + gistSelectNone: "Select none", + gistIncludeConfig: "Include config files (may contain secrets)", + gistNoPlugins: "This profile has no plugins to export", + gistSpecLocal: "local", + gistExportGo: "Export", + gistCreated: "Exported:", + gistErrTimeout: "GitHub request timed out — check your network and retry", + gistErrNetwork: "Cannot reach GitHub — check your network or proxy", + gistErrAuth: "GitHub authentication failed: invalid/expired token, or gh CLI not logged in", + gistErrNotFound: "Gist not found — check the ID or URL", + gistErrRateLimit: "GitHub rate limit hit, or the token lacks the gist scope", + gistErrInvalid: "Gist content is invalid or uses an unsupported backup format", + gistModeUpdate: "Update the Gist in the field", + gistModeCreate: "Create a new private Gist", + gistErrNoId: "Update mode needs a Gist ID — fill it in, or switch to “Create”", + gistNote: "Unlike local export (this-machine archive), Gist is for cross-machine sync: export to a private Gist, then import it on another computer with the same account to reuse your plugins. The token lives in this session's memory only and is never written to browser storage; a logged-in gh CLI or DSH_GITHUB_TOKEN skips entering it. Gists are private, 1 MB per file.", + backupWorking: "In progress…", + backupDone: "Backup uploaded", + restoreDone: "Restore complete — restart DeepSeek Harness", + restorePartial: "Restore completed, but these plugins failed to install:", + restoreUnportable: "points at an absolute path from another machine that does not exist here — repoint it locally or reinstall the plugin", + restoreBootError: "Still cannot boot after the restore:", + orphanBundle: "⚠️ The next start will fail: the profile declares these bundles but they are not installed. Reinstall them, or remove them from dsh.profile.bundles in the profile package.json:", + restoreConfirm: "Restore will overwrite this profile configuration and reinstall plugins. Continue?", + restorePreviewDone: "Backup imported. Review Installed, then start restore.", + restoreMissing: "{0} plugins from this backup are not installed", + restoreStart: "Start restore", + notInstalled: "Not installed", + themeApply: "Use", + themeActive: "Active", + themeDeactivate: "Deactivate", + themeEmpty: "No themes in the catalog yet", + themePreview: "Preview", + themePreviewLoading: "Finding preview…", + themePreviewMissing: "Preview unavailable", + themePreviewCount: "{0} previews", + themeResultCount: "{0} themes", + themeFullscreen: "Browse full screen", + themeExitFullscreen: "Exit full screen", + progressHint: "First installs download and resolve dependencies — large plugins can take 1-3 minutes", + toastReady: "installed and live", + toastTheme: "is now active. Switch any time in Settings → Plugin Market → Themes", + toastToggledOn: "is now on", + toastToggledOff: "is now off; it stopped working immediately", + gotIt: "Dismiss", + stateLive: "Active", + stateRestart: "Installed — restart to apply", + stateInert: "Installed, not active", + stateBroken: "Installed, verification failed", + stateDisabled: "Disabled", + phaseResolving: "Resolving dependencies", + phaseDownloading: "Downloading", + phaseLinking: "Linking", + phaseBuilding: "Running build scripts", + cancelling: "Cancelling…", + packagesDone: "{0} packages processed", + updatedLive: "✓ Updated — live", + partialNote: "Cancelled — some changes were applied", + reconciledNote: "Removed, but pnpm reported an error while finishing up (usually a locked file). The profile is already reconciled — no retry needed.", + actWhy: "Why not live?", + tabList: "List", + tabGroups: "Groups", + repoLink: "View the repository on GitHub", + disabledState: "Disabled", + noteAdd: "Add note", + noteEdit: "Edit note", + noteTheirs: "Original", + noteMine: "My note", + noteSave: "Save", + notePlaceholder: "Describe it in your own words. This replaces the author description.", + noteSeeTheirs: "Show the author description", + noteSeeMine: "Back to my note", + switchOnLabel: "Enabled", + enable: "Enable", + disable: "Disable", + toggleFail: "Toggle failed", + deprecatedBadge: "Deprecated", + deprecatedWarn: "This plugin is marked as deprecated by the catalog; new users are advised against installing it.", + viewReplacement: "View replacement", + installReplacement: "Install replacement", + replacementHint: "Catalog suggests", + groupNew: "New group", + groupNamePh: "Group name", + groupRename: "Rename", + groupDelete: "Delete group", + groupConfirmDelete: "Delete group?", + ungrouped: "Ungrouped", + groupAssign: "Assign", + groupRemove: "Remove", + groupEmpty: "No members yet", + groupMixed: "Partially enabled", + noGroups: "No groups yet. Create one to begin.", + groupCreate: "Create", + groupAdd: "Add plugin", + groupAddTheme: "Add theme", + groupAddEmpty: "Every installed plugin is already in this group", + tabDiagnostics: "Diagnostics", + checkIssues: "Issues found", + checkErrors: "Errors", + checkWarnings: "Warnings", + checkLoading: "Analyzing the profile…", + checkLoadFail: "Failed to load diagnostics: ", + checkProfile: "profile", + checkErrorsEmpty: "No errors", + checkWarningsEmpty: "No warnings", + checkBundles: "Plugin load order (bundles)", + checkBundlesEmpty: "No plugin layers declared", + checkOfficial: "official", + checkCommunity: "community", + checkSource: "source", + checkEntries: "entries (loader ids)", + checkPatch: "patch file", + checkDir: "directory", + checkDuplicates: "Duplicate plugin entries", + checkDuplicatesEmpty: "No duplicate plugin entries", + checkHoisted: "hoisted", + checkPeerMismatches: "Dependency version mismatches", + checkPeerEmpty: "No dependency version mismatches", + checkPeerInfo: "{0} informational entries (unconfirmed)", + checkPeerOverview: "{0} mismatch(es) · {1} informational", + checkRange: "declared range", + checkResolved: "resolved version", + checkSatisfied: "satisfied", + checkUnsatisfied: "not satisfied", + checkUnknown: "unknown", + checkPeerRisk: "Dependency risks", + checkPeerRiskEmpty: "No risk-level dependency mismatches", + checkPeerWarning: "Dependency warnings", + checkPeerWarningEmpty: "No warning-level dependency mismatches", + checkPeerInfoTier: "Dependency info", + checkPeerInfoTierEmpty: "No informational dependency entries", + checkPeerSatisfied: "Satisfied dependencies", + checkPeerSatisfiedEmpty: "No satisfied dependency entries", + peerRiskBelowMin: "below declared minimum", + peerRiskAboveMax: "above explicit maximum", + peerWarnOptional: "optional peer", + peerWarnAboveMax: "above declared upper bound", + peerInfoUnverified: "unverified", + checkMultiVersion: "Multi-version core packages", + checkMultiEmpty: "No multi-version core packages", + checkOverrides: "Overrides", + checkOverridesEmpty: "No overrides", + checkOverridden: "overrides", + checkOrphans: "Invalid config entries", + checkOrphansEmpty: "No invalid config entries", + orphanInsertNotArray: "malformed", + orphanInsertTargetMissing: "target not found", + orphanInsertTargetNotGroup: "target is not a group", + orphanIdRequired: "missing id", + orphanPatchTargetMissing: "target not found", + orphanNameMismatch: "name mismatch", + orphanReasonOther: "other", + diagExplain: "About this page", + diagExplainText: "This page checks for plugins that conflict with each other, mismatched dependencies, or wrong load order. Terms used:", + diagTermBundle: "bundle = one plugin package, applied in order", + diagTermEntry: "entry = one item a plugin registers in the running composition", + diagTermPeer: "peer dependency = another package a plugin requires from the host", + diagTermShadow: "shadowing = a plugin’s old copy covers the host’s newer one", + diagTermOrphan: "invalid entry = config references something that does not exist", + diagTermOrder: "load order = the order plugins activate; the later one may override the earlier", + orderSection: "Ordering", + orderUp: "↑", + orderDown: "↓", + orderApply: "Apply order", + orderDrag: "Drag to reorder", + orderDragHint: "Drag ⠿ to reorder. Changes are saved when you apply the order.", + orderConflicts: "before/after conflicts in the current order", + orderApplied: "✓ Applied — restart to apply", + orderSuggestApply: "Apply suggested order", + orderSuggestHint: "Suggested order (auto-sorted by before/after rules):", + orderAutoSort: "Auto-sort", + orderAlreadyOptimal: "Current order already satisfies every rule and plugin dependency — nothing to reorder", + orderTrialFail: "Static composition validation failed: {0}", + orderDiffHint: "This order would change the composition: {0} override(s), {1} orphan(s), {2} duplicate(s)", + duplicateNames: "Same-name plugins (informational only, not a conflict)", + orderReset: "Reset draft", + checkRefresh: "Re-check", + aiFix: "AI fix", + aiFixHint: "Hand the diagnostics to a new Agent session (copied to the clipboard; you decide whether to send)", + aiFixIntro: "Please help me fix the plugin issues of DeepSeek Harness (profile: {0}). Diagnostics found:", + aiFixDetect: "Step 1 — identify yourself first. Before touching anything, determine whether the process you are running in IS the DeepSeek Harness instance this profile belongs to (i.e. this diagnosed composition is the one you are running in right now). Check whether the cwd / $DSH_HOME / command line points at this profile, or whether you are running inside a deepseek-harness process. If you cannot tell, assume the answer is \"yes\" (the safe choice).", + aiFixIfSelf: "If YES — you are this harness: never change the live composition, the harness or core packages in place (don't edit cordis.patch.yml, don't reorder/enable/disable bundles, don't install/remove/upgrade a plugin, don't reinstall deps, don't upgrade/restart dsh). Three steps: 1) read-only analysis only, change nothing; 2) produce apply.sh/.bat (this fix's changes) and rollback.sh/.bat (fully reverts it); both are idempotent — apply snapshots only when none exists yet and never overwrites an existing snapshot (an existing one means apply already ran, so roll back or confirm before re-running), while rollback restores from that snapshot, so a half-run apply can still be rolled back and repeated rollback is safe; 3) have me run apply in an external terminal and paste the stdout/stderr back to you. State the reason and wait for my confirmation at each step — never run these yourself.", + aiFixScope: "If NO — you are a process outside the target harness: you may then consider reordering dsh.profile.bundles, disabling / enabling plugins, or adjusting cordis.patch.yml. Note: official bundles are fixed; state your plan before changing anything.", + aiFixConservative: "Be conservative: only fix the clear errors listed above (boot failures / duplicate entries / risk-level dependency mismatches). Treat warnings and informational entries (e.g. optional or unconfirmed peer ranges) only when they relate to a clear error. Do not perform unnecessary upgrades or reordering; explain each change and wait for confirmation.", + aiFixCopied: "Fix prompt copied — paste it into a new conversation and send when ready", + aiFixFail: "Clipboard unavailable — copy the diagnostics below manually", + catConflict: "Conflicts", + catDeps: "Dependencies", + catRisk: "Risk", + catWarn: "Warning", + catInfo: "Info", + catOrder: "Order", + /** Summary-strip tooltip for the order count (before/after rule conflicts). */ + checkOrderTip: "community bundle load order conflicts with declared before/after rules", + diagOkAll: "No conflict, dependency or ordering issues found", + presetSection: "Plugin presets", + presetHint: "Save the current bundle order and disabled list as a preset and switch anytime; applying runs a trial-start check and creates a snapshot first.", + presetName: "Preset name", + presetSave: "Save current state", + presetSaving: "Saving…", + presetSaved: "Preset saved", + presetApply: "Apply", + presetApplied: "Applied — restart to apply", + presetDelete: "Delete", + presetDeleted: "Preset deleted", + presetEmpty: "No presets saved yet", + presetNameEmpty: "Enter a preset name", + presetFail: "Preset operation failed: ", + presetListFail: "Failed to load presets: ", + presetBundleCount: "{0} bundles", + presetPreview: "Preview", + presetPreviewTitle: "Applying this preset will:", + presetReorder: "reorder {0} bundles", + presetEnable: "enable {0} plugins", + presetDisable: "disable {0} plugins", + presetNoop: "no changes", + presetPreviewFail: "Preview failed: ", + snapSection: "Snapshots & Rollback", + snapHint: "Snapshots are taken automatically before applying changes; you can also create one manually and roll back anytime.", + snapCreate: "Create snapshot", + snapCreating: "Creating…", + snapCreated: "Snapshot created", + snapEmpty: "No snapshots yet", + snapListFail: "Failed to load snapshots: ", + snapCreateFail: "Failed to create snapshot: ", + snapRestore: "Restore", + snapRestoring: "Restoring…", + snapRestored: "Restored — restart to apply", + snapRestoreFail: "Failed to restore: ", + snapRestoreConfirm: "Confirm restore", + snapRestoreConfirmText: "Restoring overwrites the current profile package.json and configuration from the snapshot; this cannot be undone (create a fresh snapshot first if unsure).", + snapFiles: "files", + snapDelete: "Delete", + snapDeleting: "Deleting…", + snapDeleted: "Snapshot deleted", + snapDeleteFail: "Failed to delete snapshot: ", + snapDeleteConfirm: "Confirm delete", + snapDeleteConfirmText: "This snapshot cannot be recovered after deletion.", + marketNoToggle: "The market itself cannot be disabled", + hostDependencyWarning: "A plugin lists known shared DSH host packages in dependencies. This may shadow the host version; the check is manifest-only and does not confirm a duplicate runtime instance:", + hostDependencyMore: "{0} more finding(s) omitted" + }; + //#endregion + //#region src/client/market-data.ts + /** One registry entry from /dsh-market/registry. */ + /** + * Resolve a market API path against the page the UI is served from. + * + * Every call used to be root-absolute (`/dsh-market/…`), which the browser + * resolves against the ORIGIN — so behind a reverse proxy that mounts dsh + * under a prefix (`https://host/app/my-dsh/`), the panel rendered and then + * every request in it went to `https://host/dsh-market/…`, missed the prefix + * rule entirely, and 404'd (#345). + * + * Anchored on `document.baseURI`, which is the directory the host serves its + * UI from. Safe for root deployments because that directory is `/` there, and + * safe generally because the dsh web UI does not use path routing — measured + * against a real dsh: `location.pathname` is `/` on the market page, not + * `/settings/...`, so the directory really is the mount point rather than + * wherever the user happens to have navigated. + */ + function api(path) { + const relative = path.replace(/^\/+/, ""); + if (typeof document === "undefined") return `/${relative}`; + return new URL(relative, document.baseURI).pathname; + } + /** Category ids for one entry, de-duplicated in declaration order. */ + function pluginCategories(plugin) { + const values = Array.isArray(plugin.category) ? plugin.category : [plugin.category]; + const categories = []; + const seen = /* @__PURE__ */ new Set(); + for (const value of values) { + if (typeof value !== "string" || value === "" || seen.has(value)) continue; + seen.add(value); + categories.push(value); + } + return categories; + } + /** + * Add active profile Bundles as presence-only catalog entries. + * + * The returned map is for catalog matching only. Update and uninstall flows + * must keep using the dependency-only map because a Bundle supplied by the + * dsh installation is not owned by the profile package manager. + */ + function installedForCatalog(installed, bundles) { + return Object.fromEntries([...bundles.map((name) => [name, "*"]), ...Object.entries(installed)]); + } + function groupSwitchState(members, disabled) { + const list = members ?? []; + if (list.length === 0) return "empty"; + let anyOn = false; + let anyOff = false; + for (const member of list) if (disabled.has(member)) anyOff = true; + else anyOn = true; + return anyOn && anyOff ? "mixed" : anyOff ? "off" : "on"; + } + function avatarColor(name) { + let hash = 0; + for (let i = 0; i < name.length; i++) hash = hash * 31 + name.charCodeAt(i) | 0; + return "hsl(" + (hash % 360 + 360) % 360 + " 55% 52%)"; + } + function readSession(key) { + try { + return JSON.parse(sessionStorage.getItem(key) || "null"); + } catch { + return null; + } + } + /** Heuristic: plugins that target a terminal surface rather than the web UI. */ + function looksTerminal(plugin, lang) { + const positiveDesc = (plugin.description && (plugin.description[lang] || plugin.description.en) || "").replace(/\b(?:no|without)\b[^.!?;:,。!?;\n]{0,80}\b(?:tui|cli|tty|terminal)\b/gi, "").replace(/(?:无需|无须|不需要|不用)[^。!?;\n]{0,48}(?:tui|cli|tty|terminal|终端|命令行)/gi, ""); + return /\b(tui|cli|tty|terminal)\b|终端|命令行/i.test(plugin.name + " " + positiveDesc); + } + /** Days per TimeRange (`all` has no cutoff and is handled by the caller). */ + const TIME_RANGE_DAYS = { + day: 1, + week: 7, + month: 30, + quarter: 90, + year: 365 + }; + /** True when `added` is a date within the last `days` days (inclusive). */ + function withinDays(added, days) { + if (added === void 0 || added === "") return false; + const time = Date.parse(added); + if (Number.isNaN(time)) return false; + const age = Date.now() - time; + return age >= 0 && age <= days * 864e5; + } + /** + * Whether a catalog entry IS the market itself. The catalog still carries + * it — nothing about the data changes, and the Installed tab still shows it + * — this is purely "a store has no reason to sell itself to someone already + * standing in it." + */ + function isMarketItself(plugin) { + return plugin.name === "dsh-market" || plugin.npm === "dshmarket"; + } + /** Normalize punctuation-separated package names and human text alike. */ + function searchText(value) { + return value.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim().replace(/\s+/g, " "); + } + /** + * Normalized catalog fields are immutable for the lifetime of one registry + * entry. Keep them with that entry so typing does not repeat unicode + * normalization across the whole catalog, while replaced catalogs remain + * collectible. The raw query is intentionally not cached: it is normalized + * once per call and would otherwise grow the cache on every keystroke. + */ + const pluginSearchTextCache = /* @__PURE__ */ new WeakMap(); + function cachedPluginSearchText(plugin, value) { + let fields = pluginSearchTextCache.get(plugin); + if (fields === void 0) { + fields = /* @__PURE__ */ new Map(); + pluginSearchTextCache.set(plugin, fields); + } + const hit = fields.get(value); + if (hit !== void 0) return hit; + const normalized = searchText(value); + fields.set(value, normalized); + return normalized; + } + /** + * Relevance within one field. Exact and prefix matches beat phrase matches; + * for a multi-word query every word must occur in the same field. + */ + function fieldRelevance(plugin, value, query, tokens, weight) { + if (!value) return 0; + const text = cachedPluginSearchText(plugin, value); + if (text === "" || !tokens.every((token) => text.includes(token))) return 0; + if (text === query) return weight + 300; + if (text.startsWith(query)) return weight + 250; + if (text.includes(query)) return weight + 200; + return weight + 150; + } + /** + * Search ranking is field-aware rather than a popularity-only filter: + * package identities outrank owners, descriptions, and categories. The + * selected popularity/date sort remains the tie-breaker between equally + * relevant entries. + */ + function pluginRelevance(plugin, query, tokens, lang, categories) { + const descriptions = plugin.description ?? {}; + const preferredLocale = descriptions[lang] ? lang : descriptions.en ? "en" : null; + const preferredDescription = descriptions[lang] || descriptions.en; + const otherDescriptions = Object.entries(descriptions).filter(([locale, value]) => locale !== preferredLocale && typeof value === "string").map(([, value]) => value); + const categoryIds = pluginCategories(plugin); + const categoryLabels = categoryIds.flatMap((category) => Object.values(categories?.[category] ?? {})); + return Math.max(fieldRelevance(plugin, plugin.name, query, tokens, 700), fieldRelevance(plugin, plugin.npm, query, tokens, 700), fieldRelevance(plugin, plugin.owner, query, tokens, 400), fieldRelevance(plugin, preferredDescription, query, tokens, 280), ...otherDescriptions.map((value) => fieldRelevance(plugin, value, query, tokens, 240)), ...categoryIds.map((value) => fieldRelevance(plugin, value, query, tokens, 180)), ...categoryLabels.map((value) => fieldRelevance(plugin, value, query, tokens, 180))); + } + /** Compare two already-filtered entries using the user's selected sort. */ + function comparePlugins(a, b, sort) { + const hasDownloads = (p) => typeof p.downloads === "number"; + if (sort === "downloads-desc") { + if (hasDownloads(a) && hasDownloads(b)) return b.downloads - a.downloads; + if (hasDownloads(a)) return -1; + if (hasDownloads(b)) return 1; + return (b.stars ?? -1) - (a.stars ?? -1); + } + if (sort === "downloads-asc") { + if (hasDownloads(a) && hasDownloads(b)) return a.downloads - b.downloads; + if (hasDownloads(a)) return -1; + if (hasDownloads(b)) return 1; + return (a.stars ?? -1) - (b.stars ?? -1); + } + if (sort === "stars-desc") return (b.stars ?? -1) - (a.stars ?? -1); + if (sort === "stars-asc") return (a.stars ?? -1) - (b.stars ?? -1); + if (sort === "added-desc") return String(b.added).localeCompare(String(a.added)); + if (sort === "added-asc") return String(a.added).localeCompare(String(b.added)); + return 0; + } + /** + * The discover list: category filter, then the published-within window, then + * relevance-ranked search across package identity / owner / every localized + * description / category ids and labels. With no search, only the selected + * sort applies, preserving the existing discover-list behaviour. + * Pure — the section renders exactly this. + */ + function visiblePlugins(plugins, options) { + const query = searchText(options.query); + const tokens = query.split(" ").filter(Boolean); + return plugins.flatMap((plugin, index) => { + if (isMarketItself(plugin)) return []; + const categories = pluginCategories(plugin); + if (options.category !== "all" && !categories.includes(options.category)) return []; + if (options.sinceDays !== void 0 && !withinDays(plugin.added, options.sinceDays)) return []; + const relevance = query === "" ? 0 : pluginRelevance(plugin, query, tokens, options.lang, options.categories); + return relevance === 0 && query !== "" ? [] : [{ + plugin, + relevance, + index + }]; + }).sort((a, b) => b.relevance - a.relevance || comparePlugins(a.plugin, b.plugin, options.sort) || a.index - b.index).map((row) => row.plugin); + } + /** The themes tab listing: theme category only, most-starred first. */ + function themePlugins(plugins) { + return plugins.filter((p) => pluginCategories(p).includes("theme")).sort((a, b) => (b.stars || 0) - (a.stars || 0)); + } + /** + * Category chip order: collapsed with an active non-'all' chip that would + * otherwise be clipped out of the two-row preview, the active one moves to + * the front so it stays visible. + * + * Reported as "点了某个分类,标签就跑到前面来了,好奇怪": the earlier version + * moved the active chip to the front unconditionally, so clicking a category + * that was ALREADY visible inside the two rows still reshuffled it — and + * every chip after it — for no reason, since nothing was at risk of being + * hidden. `visibleCount` is how many chips (the 'all' chip included) the + * two-row clip fits; a category already within that budget in its natural + * position is left exactly where it was. + * + * `visibleCount === null` (not yet measured, e.g. the very first collapsed + * render) keeps the old unconditional behaviour: with no measurement to + * check against, guaranteeing visibility is the safe default. + */ + function orderedCategories(categories, active, open, visibleCount = null) { + if (open || active === "all") return categories; + if (visibleCount !== null) { + const budget = Math.max(0, visibleCount - 1); + const naturalIndex = categories.indexOf(active); + if (naturalIndex !== -1 && naturalIndex < budget) return categories; + } + return [active, ...categories.filter((id) => id !== active)]; + } + /** + * Page-number list for the discover pager. With few pages it is simply + * 1..total; with many it windows around the current page and inserts '…' + * so a 400-plugin catalog stays a compact `1 … 4 5 6 … 17` instead of a + * long row of numbered buttons. Always begins with 1 and ends with total. + */ + function pageItems(current, total) { + if (total <= 7) { + const all = []; + for (let i = 1; i <= total; i++) all.push(i); + return all; + } + const items = [1]; + let start = Math.max(2, current - 1); + let end = Math.min(total - 1, current + 1); + if (current <= 4) end = 5; + if (current >= total - 3) start = total - 4; + if (start > 2) items.push("…"); + for (let i = start; i <= end; i++) items.push(i); + if (end < total - 1) items.push("…"); + items.push(total); + return items; + } + /** + * Unified installed-state matching (#15): both sides collapse to lowercase + * identity sets — the registry entry contributes its bare name, npm name and + * owner/repo; the dependency contributes its key and the repo inside its + * spec — and any exact intersection counts. Exact equality, not substrings, + * so prefix-related repo names cannot cross-match. + */ + /** + * Memo for entryIdentities, keyed on the catalog entry object itself. + * + * Catalog entries are parsed once and never mutated, so the identity set is + * a pure function of an object that outlives every call — a WeakMap holds + * it for exactly as long as the catalog is alive and not one render longer. + * Worth caching because this is the innermost step of the installed-state + * matching that runs for every card on screen (#262). + */ + const entryIdCache = /* @__PURE__ */ new WeakMap(); + function entryIdentities(plugin) { + const cached = entryIdCache.get(plugin); + if (cached !== void 0) return cached; + const ids = /* @__PURE__ */ new Set([plugin.name.toLowerCase()]); + if (plugin.npm) ids.add(plugin.npm.toLowerCase()); + const m = /^https:\/\/github\.com\/([^/]+\/[^/]+?)(?:\/tree\/[^/]+\/(.+?))?\/?$/.exec(plugin.url); + if (m !== null) ids.add(m[2] !== void 0 ? `${m[1].toLowerCase()}#path:/${m[2].toLowerCase()}` : m[1].toLowerCase()); + entryIdCache.set(plugin, ids); + return ids; + } + const REPO_ID_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:#path:\/[A-Za-z0-9_./-]+)?$/; + function addRepoIdentities(ids, values) { + for (const value of values) { + if (!REPO_ID_RE.test(value)) continue; + const subpath = value.split("#path:/")[1]; + if (subpath !== void 0 && subpath.split("/").some((seg) => seg === "" || seg === "." || seg === "..")) continue; + ids.add(value.toLowerCase()); + } + } + /** Repo identities carried by a github shortcut, including `#sha&path:`. */ + function githubSpecRepoIds(spec) { + const ids = /* @__PURE__ */ new Set(); + const match = /^github:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:\.git)?(?:#(.*))?$/i.exec(spec); + if (match === null) return ids; + const repo = match[1].toLowerCase(); + let subpath = null; + for (const selector of (match[2] ?? "").split("&")) { + if (!selector.startsWith("path:/")) continue; + const candidate = selector.slice(6); + if (!REPO_ID_RE.test(`${repo}#path:/${candidate}`) || candidate.split("/").some((seg) => seg === "" || seg === "." || seg === "..") || subpath !== null) return /* @__PURE__ */ new Set(); + subpath = candidate.toLowerCase(); + } + ids.add(repo); + if (subpath !== null) ids.add(`${repo}#path:/${subpath}`); + return ids; + } + function depIdentities(name, spec, repoIdentities = []) { + const ids = /* @__PURE__ */ new Set([name.toLowerCase()]); + const scoped = /^@([^/]+)\/(.+)$/.exec(name); + if (scoped !== null) ids.add(`${scoped[1].toLowerCase()}/${scoped[2].toLowerCase()}`); + for (const id of githubSpecRepoIds(spec)) ids.add(id); + addRepoIdentities(ids, repoIdentities); + return ids; + } + /** + * Repo identities stated by the dependency SPEC itself (github: installs) — + * hard evidence of where the package came from, unlike the name-derived + * mirror in depIdentities, which is only a matching aid. + */ + function depRepoIds(spec, repoIdentities = []) { + const ids = githubSpecRepoIds(spec); + addRepoIdentities(ids, repoIdentities); + return ids; + } + /** Repo identity of a registry entry's source url (repo or repo#path form). */ + function entryRepoIds(plugin) { + const ids = /* @__PURE__ */ new Set(); + const m = /^https:\/\/github\.com\/([^/]+\/[^/]+?)(?:\/tree\/[^/]+\/(.+?))?\/?$/.exec(plugin.url); + if (m !== null) ids.add(m[2] !== void 0 ? `${m[1].toLowerCase()}#path:/${m[2].toLowerCase()}` : m[1].toLowerCase()); + return ids; + } + /** + * The curated registry lists distinct plugins sharing one name — twelve + * name-groups at the time of #66 (both dsh-usage-stats, four dsh-memory…). + * A name coincidence must not survive contradicting repo evidence: when the + * dependency's spec pins a github repo AND the entry states one, the repos + * decide — the loose name/npm identities only apply when at least one side + * carries no repo evidence (npm installs, non-github entries). + */ + function sameSourceConflict(plugin, spec, repoIdentities = []) { + const entry = entryRepoIds(plugin); + const dep = depRepoIds(spec, repoIdentities); + if (entry.size === 0 || dep.size === 0) return false; + for (const id of dep) if (entry.has(id)) return false; + return true; + } + function repoHintMatches(plugin, hints) { + const entry = entryRepoIds(plugin); + const values = /* @__PURE__ */ new Set(); + addRepoIdentities(values, hints); + for (const id of values) if (entry.has(id)) return true; + return false; + } + /** + * Memo for looseMatchCount, keyed on the catalog array then the dep name. + * + * This is THE hot path behind "the plugin list is very laggy" (#262). The + * count answers "how many catalog entries could this installed dependency + * be?", which depends only on the catalog and the name — not on the card + * being drawn. But it was called from matchInstalledName, which runs once + * per installed dependency, which runs once per rendered card: a full scan + * of ~1800 entries, repeated cards × installed times, on every single + * render. A profile from the reporter put it at 2.9 seconds, 28% of the + * whole trace, and a local benchmark measured 48ms per render at 24 cards + * and 224ms at 96 against a smaller 839-entry catalog. + * + * Keyed on the array identity so a refetched catalog gets a fresh map for + * free — a new parse is a new array, and the old one is collectable. + */ + const looseMatchCountCache = /* @__PURE__ */ new WeakMap(); + function looseMatchCount(plugins, name) { + let byName = looseMatchCountCache.get(plugins); + if (byName === void 0) { + byName = /* @__PURE__ */ new Map(); + looseMatchCountCache.set(plugins, byName); + } + const hit = byName.get(name); + if (hit !== void 0) return hit; + const dep = depIdentities(name, ""); + let count = 0; + for (const plugin of plugins) for (const id of entryIdentities(plugin)) if (dep.has(id)) { + count += 1; + break; + } + byName.set(name, count); + return count; + } + function looseMatches(plugin, name) { + const dep = depIdentities(name, ""); + for (const id of entryIdentities(plugin)) if (dep.has(id)) return true; + return false; + } + /** The installed dependency name a registry entry corresponds to, or null. */ + function matchInstalledName(plugin, installed, repoIdentities = {}, plugins, repoHints = {}) { + const ids = entryIdentities(plugin); + for (const [name, spec] of Object.entries(installed)) { + const repos = repoIdentities[name] ?? []; + if (depRepoIds(String(spec), repos).size === 0 && plugins !== void 0 && looseMatchCount(plugins, name) > 1 && !repoHintMatches(plugin, repoHints[name] ?? [])) continue; + if (sameSourceConflict(plugin, String(spec), repos)) continue; + for (const id of depIdentities(name, String(spec), repos)) if (ids.has(id)) return name; + } + return null; + } + /** The registry entry an installed dependency corresponds to, or undefined. */ + function entryForDep(plugins, name, spec, repoIdentities = [], repoHints = []) { + if (depRepoIds(String(spec), repoIdentities).size === 0 && looseMatchCount(plugins, name) > 1) { + if (plugins.find((plugin) => repoHintMatches(plugin, repoHints) && looseMatches(plugin, name)) === void 0) return void 0; + } + const ids = depIdentities(name, String(spec), repoIdentities); + return plugins.find((plugin) => { + if (sameSourceConflict(plugin, String(spec), repoIdentities)) return false; + for (const id of entryIdentities(plugin)) if (ids.has(id)) return true; + return false; + }); + } + function isInstalled(plugin, installed, repoIdentities = {}, plugins, repoHints = {}) { + return matchInstalledName(plugin, installed, repoIdentities, plugins, repoHints) !== null; + } + /** + * The header brand mark now lives in MarketSection.tsx as an inline SVG + * (official-style monochrome glyph, fill="currentColor") so it follows the + * active theme; the colored assets/logo.svg tile is no longer inlined here. + */ + /** Four representative colors for a theme card's preview strip. */ + function themeSwatch(def) { + const tk = def.tokens || {}; + const pick = (names) => { + for (const n of names) if (tk[n]) return tk[n]; + return null; + }; + const dark = def.colorScheme === "dark"; + return [ + pick(["--dsw-alias-bg-base", "--dsw-alias-bg-layer-1"]) || (dark ? "#0f1115" : "#ffffff"), + pick(["--dsw-alias-bg-layer-2", "--dsw-alias-bg-overlay"]) || (dark ? "#1a1d23" : "#f3f4f6"), + pick(["--dsw-alias-brand-primary"]) || "#4f6ef7", + pick(["--dsw-alias-label-primary"]) || (dark ? "#e5e7eb" : "#1f2328") + ]; + } + /** + * Prefix for github.com URLs this page loads, or null to address them + * directly. Set from the status poll, which gets it from the download region. + * + * Module state rather than a prop: the URLs it applies to are built in four + * places across two files (avatars, README fetches, screenshot thumbnails), + * and threading one string through every card would put it in signatures + * that have no other reason to know about networking. + * + * Applied at the LAST moment, never stored. Extracted image URLs stay + * canonical, so changing region re-renders against the new route instead of + * leaving a page full of links to a proxy the user just switched away from. + */ + let githubProxy = null; + /** Point browser-side github.com requests at a proxy, or null for direct. */ + function setGithubProxy(proxy) { + githubProxy = proxy; + } + /** The proxy in force, for callers that must decide between two URL shapes. */ + function githubProxyInUse() { + return githubProxy; + } + /** `url` through the proxy in force, or unchanged when there is none. */ + function githubUrl(url) { + return githubProxy === null ? url : `${githubProxy}/${url}`; + } + /** + * Image hosts screenshots may load from (#61) — GitHub's own hosting only. + * Any other host is dropped BEFORE an is created: a screenshot URL is + * a request carrying the user's IP, so registry data and README content are + * both treated as untrusted here, matching the upstream build gate. + */ + const SCREENSHOT_HOSTS = /* @__PURE__ */ new Set([ + "raw.githubusercontent.com", + "user-images.githubusercontent.com", + "camo.githubusercontent.com", + "github.com" + ]); + const MAX_SCREENSHOTS = 6; + /** Return one safe screenshot URL without applying the public list limit. */ + function safeScreenshot(value) { + if (typeof value !== "string") return null; + let parsed; + try { + parsed = new URL(value); + } catch { + return null; + } + if (parsed.protocol !== "https:" || !SCREENSHOT_HOSTS.has(parsed.hostname)) return null; + if (/\.svg$/iu.test(parsed.pathname)) return null; + return value; + } + /** Keep only https URLs on allowlisted image hosts; SVG dropped (logos/badges). */ + function safeScreenshots(urls) { + if (!Array.isArray(urls)) return []; + const safe = []; + for (const value of urls) { + const src = safeScreenshot(value); + if (src === null) continue; + if (!safe.includes(src)) safe.push(src); + if (safe.length >= MAX_SCREENSHOTS) break; + } + return safe; + } + const PREVIEW_WORDS = /(?:preview|screen[ -]?shots?|shots?|demo|showcase|gallery|theme|skin|appearance|效果|预览|截图|演示|展示|界面|主题|皮肤)/iu; + const FULL_PREVIEW_WORDS = /(?:full|overview|home|main|conversation|chat|workspace|dashboard|完整|主页|首页|全景|主界面)/iu; + const PARTIAL_PREVIEW_WORDS = /(?:settings?|panel|dialog|modal|picker|menu|controls?|fragment|crop|detail|配置|设置|面板|弹窗|局部|细节)/iu; + const NON_PREVIEW_WORDS = /(?:badge|shield|logo|icon|avatar|sponsor|donat|fund|qr(?:code)?|wechat|qq(?:group)?|npm|build|coverage|license|status|button|favicon|徽章|图标|头像|赞助|捐赠|二维码|微信|交流群)/iu; + /** A quoted or unquoted HTML attribute; README HTML is data, never rendered. */ + function htmlAttribute(html, name) { + const match = new RegExp(`\\b${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "iu").exec(html); + return match?.[1] ?? match?.[2] ?? match?.[3] ?? ""; + } + function numericDimension(raw) { + if (!/^\d+(?:\.\d+)?$/u.test(raw.trim())) return null; + const value = Number(raw); + return Number.isFinite(value) && value > 0 ? value : null; + } + /** Resolve one README image path to the canonical GitHub-hosted URL. */ + function resolveReadmeImage(raw, owner, repo, base) { + const src = raw.trim().replace(/^<|>$/g, ""); + if (src === "" || src.startsWith("data:")) return null; + let absolute; + if (/^https?:\/\//iu.test(src)) absolute = src; + else if (src.startsWith("/")) absolute = `https://raw.githubusercontent.com/${owner}/${repo}/HEAD${src}`; + else try { + absolute = new URL(src, base).href; + } catch { + return null; + } + return safeScreenshot(absolute); + } + /** Score evidence available without downloading the image itself. */ + function readmeSemanticScore(image, heading, nearby, order, offset) { + const label = `${image.alt} ${image.title}`; + const path = (() => { + try { + const parsed = new URL(image.src); + if (parsed.hostname === "raw.githubusercontent.com") return "/" + parsed.pathname.split("/").slice(4).join("/"); + return parsed.pathname; + } catch { + return image.src; + } + })(); + let score = 20 + Math.max(0, 8 - order); + if (PREVIEW_WORDS.test(label)) score += 55; + if (PREVIEW_WORDS.test(path)) score += 40; + if (PREVIEW_WORDS.test(heading)) score += 32; + if (PREVIEW_WORDS.test(nearby)) score += 12; + if (FULL_PREVIEW_WORDS.test(`${label} ${path}`)) score += 35; + if (PARTIAL_PREVIEW_WORDS.test(`${label} ${path}`)) score -= 30; + if (NON_PREVIEW_WORDS.test(label)) score -= 140; + if (NON_PREVIEW_WORDS.test(path)) score -= 120; + if (NON_PREVIEW_WORDS.test(heading)) score -= 55; + if (NON_PREVIEW_WORDS.test(nearby)) score -= 18; + if (offset < 500 && !PREVIEW_WORDS.test(`${label} ${path} ${heading}`)) score -= 20; + if (image.width !== null && image.height !== null) score += previewDimensionScore(image.width, image.height) ?? -500; + else if ((image.width ?? image.height ?? Number.POSITIVE_INFINITY) < 240) score -= 100; + return score; + } + /** + * Ranked README image candidates for use when the catalog has no curated + * screenshots. Ranking uses the image label/path, nearest heading, nearby + * prose, declared dimensions and document position. This prevents a title + * logo or a row of tiny badges from consuming the six-candidate limit before + * a later Screenshots section is reached. + */ + function extractReadmeImageCandidates(markdown, owner, repo, subpath) { + const base = `https://raw.githubusercontent.com/${owner}/${repo}/HEAD/${subpath === null ? "" : subpath + "/"}`; + const headings = [...markdown.matchAll(/^#{1,6}\s+(.+?)\s*#*\s*$/gmu)].map((match) => ({ + offset: match.index, + text: match[1] ?? "" + })); + const found = /* @__PURE__ */ new Map(); + let headingIndex = -1; + let order = 0; + for (const match of markdown.matchAll(/!\[([^\]]*)\]\(\s*(?:<([^>]+)>|([^\s)]+))(?:\s+(?:"([^"]*)"|'([^']*)'|\(([^)]*)\)))?\s*\)|]*?)\/?\s*>/gimu)) { + while (headingIndex + 1 < headings.length && headings[headingIndex + 1].offset < match.index) headingIndex += 1; + const html = match[7] ?? ""; + const src = resolveReadmeImage(match[2] ?? match[3] ?? htmlAttribute(html, "src"), owner, repo, base); + if (src === null) continue; + const candidate = { + src, + semanticScore: readmeSemanticScore({ + src, + alt: match[1] ?? htmlAttribute(html, "alt"), + title: match[4] ?? match[5] ?? match[6] ?? htmlAttribute(html, "title"), + width: numericDimension(htmlAttribute(html, "width")), + height: numericDimension(htmlAttribute(html, "height")) + }, headings[headingIndex]?.text ?? "", markdown.slice(Math.max(0, match.index - 100), Math.min(markdown.length, match.index + match[0].length + 100)), order, match.index), + order, + curated: false + }; + const previous = found.get(src); + if (previous === void 0 || candidate.semanticScore > previous.semanticScore) found.set(src, candidate); + order += 1; + } + return [...found.values()].filter((candidate) => candidate.semanticScore >= 20).sort((a, b) => b.semanticScore - a.semanticScore || a.order - b.order).slice(0, MAX_SCREENSHOTS); + } + /** + * Score dimensions from a 240px-high, no-upscale probe. + * + * A theme preview should resemble a complete desktop surface: landscape, + * neither a narrow crop nor a panoramic strip, and large enough to inspect. + * Small square logos and portrait fragments intentionally return null. + */ + function previewDimensionScore(width, height) { + if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return null; + const ratio = width / height; + const area = width * height; + if (width < 280 || height < 150 || area < 48e3 || ratio < 1.05 || ratio > 3.2) return null; + let score = Math.min(28, Math.round(area / 4e3)); + if (ratio >= 1.35 && ratio <= 2.05) score += 48; + else if (ratio >= 1.18 && ratio <= 2.4) score += 28; + else score += 8; + if (width >= 320 && height >= 180) score += 14; + return score; + } + /** Combine README semantics with measured geometry and return the best set. */ + function rankThemeScreenshots(candidates, measurements) { + const bySrc = new Map(measurements.map((item) => [item.src, item])); + return candidates.flatMap((candidate) => { + const measured = bySrc.get(candidate.src); + if (measured === void 0) return []; + const dimensionScore = previewDimensionScore(measured.width, measured.height); + return dimensionScore === null ? [] : [{ + candidate, + score: candidate.semanticScore + dimensionScore + }]; + }).sort((a, b) => b.score - a.score || a.candidate.order - b.candidate.order).slice(0, MAX_SCREENSHOTS).map((item) => item.candidate.src); + } + const readmeShotsCache = /* @__PURE__ */ new Map(); + /** + * Screenshot candidates for a plugin: the registry's curated list when + * present, otherwise lazily extracted and semantically ranked from README. + */ + function pluginScreenshotCandidates(plugin) { + const curated = safeScreenshots(plugin.screenshots); + if (curated.length > 0) return Promise.resolve(curated.map((src, order) => ({ + src, + order, + semanticScore: 1e3 - order, + curated: true + }))); + const m = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\/tree\/[^/]+\/(.+?))?\/?$/.exec(plugin.url); + if (m === null) return Promise.resolve([]); + const [, owner, repo, subpath = null] = m; + const cacheKey = plugin.url; + const cached = readmeShotsCache.get(cacheKey); + if (cached !== void 0) return cached; + const fetchReadme = async (path) => { + try { + const res = await fetch(githubUrl(`https://raw.githubusercontent.com/${owner}/${repo}/HEAD/${path === null ? "" : path + "/"}README.md`)); + return res.ok ? await res.text() : null; + } catch { + return null; + } + }; + const task = (async () => { + const sub = subpath === null ? null : await fetchReadme(subpath); + if (sub !== null) return extractReadmeImageCandidates(sub, owner, repo, subpath); + const root = await fetchReadme(null); + return root === null ? [] : extractReadmeImageCandidates(root, owner, repo, null); + })().catch(() => []); + readmeShotsCache.set(cacheKey, task); + return task; + } + /** Screenshot URLs for dialogs; theme covers use the richer candidate API. */ + async function pluginScreenshots(plugin) { + return (await pluginScreenshotCandidates(plugin)).map((candidate) => candidate.src); + } + /** + * The human-readable part of a failed command's output. + * + * pnpm's ndjson reporter writes one JSON object per progress tick, and a + * large `github:` download emits thousands of them. When a failure matches + * none of the known signatures there is no diagnosis to show, so the UI + * falls back to the tail of stdout/stderr — which for exactly that case is + * 600 characters of `{"name":"pnpm:fetching-progress","downloaded":…}`. + * The user is handed machine noise at the one moment they need a sentence + * (#148, and the same shape behind #161). + * + * Progress objects are dropped; anything else — including JSON carrying a + * real message — is kept, because an unrecognized failure is precisely when + * throwing information away is most expensive. + */ + function humanOutput(raw) { + const lines = raw.split(/\r?\n/); + const kept = []; + for (const line of lines) { + const trimmed = line.trim(); + if (trimmed === "") continue; + if (!trimmed.startsWith("{")) { + kept.push(line); + continue; + } + try { + const parsed = JSON.parse(trimmed); + const name = typeof parsed.name === "string" ? parsed.name : ""; + if (parsed.err !== void 0 || typeof parsed.message === "string") { + kept.push(line); + continue; + } + if (name.startsWith("pnpm:")) continue; + kept.push(line); + } catch { + kept.push(line); + } + } + return kept.join("\n").trim(); + } + /** + * The plugin's own name, for display. + * + * The catalog's `name` is an IDENTITY, and for the 104 entries that live in + * a repository holding several plugins it is a compound one: + * `dsh-web#packages/dsh-web-all`. Shown verbatim it puts a repository + * path in front of a user who did not ask about repositories — and worse, it + * disagrees with the market's own installed list, which reads names out of + * the profile manifest and calls the same plugin `dsh-web-all`. The same + * thing had two names either side of the Install button. + * + * A card answers two questions: who made it, and what is it called. The + * author is drawn beside their avatar as one unit, so the title is free to + * be just the plugin. Duplicate titles across authors are fine — the byline + * is what separates them — which is why this does not try to keep the + * repository as a qualifier. + * + * The repository name IS the plugin name in the ordinary case, because a + * repository holding one plugin is named after it. Only the compound form + * needs unpicking, and its last segment is the plugin's own directory. + * + * Not a substitute for the identity: every key, lookup and install still + * uses `name` unchanged. + */ + function pluginName(name) { + const hash = name.indexOf("#"); + if (hash === -1) return name; + const sub = name.slice(hash + 1); + const leaf = sub.slice(sub.lastIndexOf("/") + 1); + return leaf === "" ? name.slice(0, hash) : leaf; + } + /** + * Compact display for a count that can run into the tens of thousands + * (npm downloads, star counts): "11.9k" instead of "11862". Reported — + * the raw number made the card byline visibly cramped once downloads was + * added alongside stars. + * + * Below 1000 the exact number is shown; a small count is exactly the case + * where the precision matters and abbreviating it buys nothing. + */ + function formatCount(n) { + if (!Number.isFinite(n) || n < 1e3) return String(n); + const k = Math.round(n / 100) / 10; + return `${Number.isInteger(k) ? k.toFixed(0) : k.toFixed(1)}k`; + } + //#endregion + //#region src/client/InstallToast.tsx + /** + * Post-reload confirmation via the official Toast primitive: shown once after + * the refresh that follows a hot install or theme switch, so the user lands + * back in their flow with visible proof. + */ + function InstallToast(props) { + const t = props.t; + const [mode] = (0, react.useState)(() => { + const value = sessionStorage.getItem("dshm-toast-mode"); + sessionStorage.removeItem("dshm-toast-mode"); + return value; + }); + const [names, setNames] = (0, react.useState)(() => { + const value = readSession("dshm-toast"); + sessionStorage.removeItem("dshm-toast"); + return Array.isArray(value) ? value : []; + }); + if (names.length === 0) return null; + return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, { + text: names.join(", ") + " " + t(mode === "theme" ? "toastTheme" : "toastReady"), + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSparkle16, { size: 14 }), + onDone: () => setNames([]) + }); + } + //#endregion + //#region \0dsh-css:src/client/Market.module.css.mjs + const css = ".nUhMVa_root{min-width:0;height:100%;color:var(--dsw-alias-label-primary,#1f2328);flex-direction:column;display:flex;position:relative;container-type:inline-size}.nUhMVa_head{flex-direction:column;gap:12px;padding:4px 4px 6px;display:flex}.nUhMVa_title{margin:0;font-size:16px;font-weight:500;line-height:24px}.nUhMVa_sub{color:var(--dsw-alias-label-tertiary,#8b93a1);align-items:center;gap:8px;margin:0;font-size:12px;line-height:18px;display:flex}.nUhMVa_submitLink{color:var(--dsw-alias-label-tertiary,#8b93a1);white-space:nowrap;font-size:11px;line-height:18px;text-decoration:none}.nUhMVa_submitLink:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);text-decoration:underline}.nUhMVa_tabs{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:flex-end;gap:2px;display:flex}.nUhMVa_tab{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;padding:7px 12px;font-size:13px}.nUhMVa_tab.nUhMVa_on{color:var(--dsw-alias-brand-primary,#4f6ef7);border-bottom-color:var(--dsw-alias-brand-primary,#4f6ef7);font-weight:600}.nUhMVa_subTabs{align-items:flex-end;gap:2px;margin:-4px 0 4px;display:flex}.nUhMVa_banner{background:var(--dsw-alias-bg-layer-2,#fdf3e3);border:1px solid var(--dsw-alias-border-l2,#f3e3c3);border-radius:8px;align-items:center;gap:8px;margin:0;padding:8px 12px;font-size:12px;display:flex}.nUhMVa_bannerIcon{color:var(--dsw-alias-label-secondary,#6b7280);flex-shrink:0}.nUhMVa_bannerHint{color:var(--dsw-alias-label-tertiary,#8b93a1);cursor:help;display:inline-flex}.nUhMVa_body{overflow-anchor:none;flex:1;padding:12px 4px 24px;overflow-x:hidden;overflow-y:auto}.nUhMVa_stickyHead{z-index:5;background:var(--dsw-alias-bg-layer-2,#f7f8fa);position:sticky;top:-1px}.nUhMVa_stickyHead:before{content:\"\";background:inherit;pointer-events:none;height:14px;position:absolute;bottom:100%;left:0;right:0}.nUhMVa_cats{margin:0 -4px 2px;padding:12px 4px 4px}.nUhMVa_catsRow{align-items:flex-start;gap:8px;display:flex;position:relative}.nUhMVa_star{color:var(--dsw-alias-label-secondary,#9ca3af);white-space:nowrap;flex:none;font-size:11px}.nUhMVa_top{z-index:20;display:inline-flex;position:absolute;bottom:18px;right:18px}.nUhMVa_topBtn{border-radius:99px;width:38px;height:38px;padding:0}.nUhMVa_tag{border:1px solid var(--dsw-alias-border-l3,#d9dde3);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:4px;flex-shrink:0;padding:1px 6px;font-size:11px;line-height:16px}.nUhMVa_okState{color:var(--dsw-alias-state-success-primary,#16a34a);white-space:nowrap;font-size:12px;font-weight:600}.nUhMVa_catsWrap{flex-wrap:wrap;flex:1;align-items:center;gap:6px;min-width:0;display:flex}.nUhMVa_catsCollapsed{max-height:62px;overflow:hidden}.nUhMVa_catsToggle.nUhMVa_catsToggle{height:26px;min-height:26px;color:var(--dsw-alias-label-secondary,#6b7280);padding:0 6px}.nUhMVa_shots{-webkit-overflow-scrolling:touch;scrollbar-width:thin;gap:8px;margin:6px 0 8px;padding:2px 0 6px;display:flex;overflow-x:auto}.nUhMVa_shot{object-fit:cover;border:1px solid var(--dsw-alias-border-default,#e5e7eb);background:var(--dsw-alias-bg-layer-2,#f3f4f6);cursor:pointer;border-radius:8px;flex:none;width:220px;height:150px}.nUhMVa_cardShots{-webkit-overflow-scrolling:touch;scrollbar-width:thin;gap:6px;margin:0 0 6px;padding:0 0 2px;display:flex;overflow-x:auto}.nUhMVa_cardShot{object-fit:contain;border:1px solid var(--dsw-alias-border-default,#e5e7eb);background:var(--dsw-alias-bg-layer-2,#f3f4f6);cursor:pointer;border-radius:8px;flex:none;width:132px;height:88px;display:block}.nUhMVa_lightbox{z-index:10000;cursor:zoom-out;background:#000000d9;justify-content:center;align-items:center;display:flex;position:fixed;top:0;bottom:0;left:0;right:0}.nUhMVa_lightboxImg{object-fit:contain;cursor:default;border-radius:4px;max-width:90vw;max-height:85vh}.nUhMVa_lightboxClose{color:#fff;cursor:pointer;background:#ffffff1f;border:none;border-radius:99px;place-items:center;width:36px;height:36px;font-size:22px;line-height:1;display:grid;position:absolute;top:16px;right:16px}.nUhMVa_lightboxClose:hover{background:#ffffff38}.nUhMVa_lightboxNav{color:#fff;cursor:pointer;background:#ffffff1f;border:none;border-radius:99px;place-items:center;width:44px;height:44px;display:grid;position:absolute;top:50%;transform:translateY(-50%)}.nUhMVa_lightboxNav:hover{background:#ffffff38}.nUhMVa_lightboxPrev{left:16px}.nUhMVa_lightboxNext{right:16px}.nUhMVa_lightboxDots{gap:8px;display:flex;position:absolute;bottom:20px;left:50%;transform:translate(-50%)}.nUhMVa_lightboxDot{cursor:pointer;background:#fff6;border-radius:99px;width:7px;height:7px}.nUhMVa_lightboxDotOn{background:#fff}.nUhMVa_cmd{background:var(--dsw-alias-bg-layer-2,#f3f4f6);word-break:break-all;border-radius:6px;margin:8px 0 0;padding:8px 10px;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:18px}.nUhMVa_warnLine{color:var(--dsw-alias-state-warn-primary,#b45309);flex-wrap:wrap;align-items:center;gap:4px;margin:0;font-size:12px;font-weight:600;line-height:18px;display:flex}.nUhMVa_modalNote{color:var(--dsw-alias-label-tertiary,#8b93a1);align-items:center;gap:4px;margin:12px 0 0;font-size:12px;line-height:18px;display:flex}.nUhMVa_grid{grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;gap:10px;display:grid}.nUhMVa_masonry{align-items:flex-start;gap:10px;display:flex}.nUhMVa_masonryCol{flex-direction:column;flex:1;gap:10px;min-width:0;display:flex}.nUhMVa_masonry>.nUhMVa_masonryCol>*{align-self:stretch;min-width:0}@media (max-width:680px){.nUhMVa_masonry{flex-direction:column}.nUhMVa_masonryCol{width:100%}.nUhMVa_grid{grid-template-columns:minmax(0,1fr)}}.nUhMVa_swatches{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;gap:0;height:34px;display:flex;overflow:hidden}.nUhMVa_themesGrid{margin-bottom:12px}.nUhMVa_swatches i{flex:1}.nUhMVa_themeToolbar{grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px;padding:0 4px 12px;display:grid}.nUhMVa_themeSearch{box-sizing:border-box;width:100%;min-width:0}.nUhMVa_themeToolbarActions{justify-content:flex-end;align-items:center;gap:8px;display:flex}.nUhMVa_themeFullscreenBtn.nUhMVa_themeFullscreenBtn{width:28px;min-width:28px;padding:0}.nUhMVa_themeResultBar{min-height:24px;color:var(--dsw-alias-label-tertiary,#8b93a1);align-items:center;padding:0 4px 8px;font-size:12px;line-height:18px;display:flex}.nUhMVa_themeGallery{grid-template-columns:repeat(4,minmax(0,1fr));align-items:stretch;gap:10px;display:grid}.nUhMVa_themeCard{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);border-radius:8px;flex-direction:column;min-width:0;transition:border-color .16s cubic-bezier(.16,1,.3,1),box-shadow .16s cubic-bezier(.16,1,.3,1),transform .16s cubic-bezier(.16,1,.3,1);display:flex;overflow:hidden}.nUhMVa_themeCard:hover{border-color:var(--dsw-alias-border-l3,#d9dde3);box-shadow:var(--dsw-shadow-lv1,0 4px 12px #1f232814);transform:translateY(-1px)}.nUhMVa_themeCover{aspect-ratio:16/10;border:0;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-2,#f3f4f6);width:100%;color:var(--dsw-alias-label-secondary,#6b7280);cursor:zoom-in;border-radius:0;padding:0;display:block;position:relative;overflow:hidden}.nUhMVa_themeCover img{object-fit:contain;background:var(--dsw-alias-bg-layer-2,#f3f4f6);width:100%;height:100%;transition:transform .18s cubic-bezier(.16,1,.3,1);display:block}.nUhMVa_themeCover:hover img{transform:scale(1.012)}.nUhMVa_themeCover:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4f6ef7);outline-offset:-3px}.nUhMVa_themeCoverEmpty{cursor:default;color:var(--dsw-alias-label-tertiary,#8b93a1);flex-direction:column;justify-content:center;align-items:center;gap:8px;font-size:12px;line-height:18px;display:flex}.nUhMVa_themePreviewAction,.nUhMVa_themePreviewCount{color:#fff;-webkit-backdrop-filter:blur(8px);background:#14181fc7;border:1px solid #ffffff57;border-radius:4px;align-items:center;height:24px;padding:0 8px;font-size:11px;line-height:16px;display:inline-flex;position:absolute;bottom:8px}.nUhMVa_themePreviewAction{gap:4px;right:8px}.nUhMVa_themePreviewCount{left:8px}.nUhMVa_themeCardBody{flex-direction:column;flex:1;gap:8px;padding:12px;display:flex}.nUhMVa_themeCardHead{align-items:flex-start;gap:8px;min-width:0;display:flex}.nUhMVa_themeIdentity{flex:1;min-width:0}.nUhMVa_themeStatus,.nUhMVa_themeStatusMuted{white-space:nowrap;border-radius:4px;flex:none;align-items:center;min-height:22px;padding:0 8px;font-size:11px;font-weight:600;display:inline-flex}.nUhMVa_themeStatus{background:var(--dsw-alias-state-success-tertiary,#16a34a1a);color:var(--dsw-alias-state-success-primary,#15803d)}.nUhMVa_themeStatusMuted{background:var(--dsw-alias-bg-layer-2,#f3f4f6);color:var(--dsw-alias-label-secondary,#6b7280)}.nUhMVa_themeDescription{min-height:36px;color:var(--dsw-alias-label-tertiary,#8b93a1);-webkit-line-clamp:3;-webkit-box-orient:vertical;margin:0;font-size:12px;line-height:18px;display:-webkit-box;overflow:hidden}.nUhMVa_themeCardFooter{justify-content:space-between;align-items:center;gap:8px;margin-top:auto;padding-top:4px;display:flex}.nUhMVa_themeLifecycle{min-width:0;color:var(--dsw-alias-label-tertiary,#8b93a1);text-overflow:ellipsis;white-space:nowrap;font-size:11px;line-height:18px;overflow:hidden}.nUhMVa_themeActions{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;margin-left:auto;display:flex}@container (width<=900px){.nUhMVa_themeGallery{grid-template-columns:repeat(3,minmax(0,1fr))}}@container (width<=680px){.nUhMVa_themeGallery{grid-template-columns:repeat(2,minmax(0,1fr))}}@container (width<=460px){.nUhMVa_themeGallery{grid-template-columns:minmax(0,1fr)}}@container (width<=420px){.nUhMVa_sub{grid-template-columns:minmax(0,1fr) auto;align-items:start;gap:2px 8px;display:grid}.nUhMVa_sub>span:first-child{grid-area:1/1}.nUhMVa_sub>.nUhMVa_grow{display:none}.nUhMVa_submitLink{grid-area:2/1}.nUhMVa_exportLogBtn{flex-shrink:0;grid-area:1/2/span 2}.nUhMVa_themeToolbar{grid-template-columns:minmax(0,1fr)}.nUhMVa_themeCardHead{flex-wrap:wrap}.nUhMVa_themeStatus,.nUhMVa_themeStatusMuted{order:3}}@container (width<=280px){.nUhMVa_themeCardFooter{flex-direction:column;align-items:flex-start}.nUhMVa_themeActions{justify-content:flex-start;width:100%;margin-left:0}}@media (max-width:560px){[role=dialog]:has([data-dsh-market-root])>nav{display:none}[role=dialog]:has([data-dsh-market-root])>div{flex:100%;width:100%;min-width:0}}[role=dialog]:has([data-dsh-market-fullscreen=true]){border-radius:0;width:100vw;max-width:none;height:100vh;max-height:none;position:fixed;top:0;bottom:0;left:0;right:0}@media (prefers-reduced-motion:reduce){.nUhMVa_themeCard,.nUhMVa_themeCover img{transition:none}.nUhMVa_themeCard:hover{transform:none}}.nUhMVa_card{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-direction:column;align-self:start;gap:12px;padding:12px 14px;display:flex}.nUhMVa_row1{align-items:flex-start;gap:10px;min-width:0;display:flex}.nUhMVa_cardAction{flex-shrink:0;align-items:center;display:inline-flex}.nUhMVa_installBtn.nUhMVa_installBtn{min-width:64px}.nUhMVa_av{color:#fff;object-fit:cover;background:var(--dsw-alias-bg-layer-2,#f3f4f6);border-radius:50%;flex-shrink:0;place-items:center;width:16px;height:16px;font-size:9px;font-weight:700;display:grid}.nUhMVa_nm{text-overflow:ellipsis;white-space:nowrap;font-size:15px;font-weight:600;line-height:22px;overflow:hidden}.nUhMVa_nmLink{color:inherit;text-decoration:none;display:block}.nUhMVa_nmLink:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);text-decoration:underline}.nUhMVa_repoMark{color:var(--dsw-alias-label-secondary,#6b7280);vertical-align:-1px;flex-shrink:0;margin-left:5px;display:inline-block}.nUhMVa_nmLink:hover .nUhMVa_repoMark{color:var(--dsw-alias-brand-primary,#4f6ef7)}.nUhMVa_byline{flex-wrap:wrap;align-items:center;gap:6px;min-width:0;margin-top:2px;display:flex}.nUhMVa_meta{color:var(--dsw-alias-label-secondary,#9ca3af);margin-top:2px;font-size:11px}.nUhMVa_metaInline{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.nUhMVa_owner{color:var(--dsw-alias-label-secondary,#9ca3af);text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:44px;font-size:11px;overflow:hidden}.nUhMVa_desc{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:0;font-size:12px;line-height:18px}.nUhMVa_descClamp{-webkit-line-clamp:5;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.nUhMVa_descToggle{width:20px;height:16px;color:var(--dsw-alias-label-tertiary,#8b93a1);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;margin-top:2px;padding:0;display:flex}.nUhMVa_descToggle:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.nUhMVa_foot{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.nUhMVa_foot .nUhMVa_metaInline,.nUhMVa_foot .nUhMVa_src{white-space:nowrap}.nUhMVa_grow{flex:1}.nUhMVa_titleRow{align-items:center;gap:10px;display:flex}.nUhMVa_version{color:var(--dsw-alias-label-tertiary,#8b93a1);font-variant-numeric:tabular-nums;flex-shrink:0;font-size:12px;line-height:20px}.nUhMVa_repoLink{color:var(--dsw-alias-label-tertiary,#8b93a1);flex-shrink:0;font-size:12px;line-height:20px;text-decoration:none}.nUhMVa_repoLink:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.nUhMVa_noteRow{flex-wrap:wrap;align-items:baseline;gap:6px;min-width:0;display:flex}.nUhMVa_noteMine{color:var(--dsw-alias-label-primary,#1f2328)}.nUhMVa_noteToggle{font:inherit;color:var(--dsw-alias-label-tertiary,#8b93a1);cursor:pointer;white-space:nowrap;background:0 0;border:none;flex-shrink:0;padding:0;font-size:11px;line-height:16px}.nUhMVa_noteToggle:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);text-decoration:underline}.nUhMVa_noteAction{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l3,#d9dde3);border-radius:4px;align-items:center;padding:0 5px;font-weight:600;text-decoration:none;display:inline-flex}.nUhMVa_noteAction:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-brand-primary,#4f6ef7);text-decoration:none}.nUhMVa_noteEdit{flex-wrap:wrap;align-items:center;gap:6px;min-width:0;margin-top:2px;display:flex}.nUhMVa_noteInput{flex:1;min-width:160px}.nUhMVa_descTight{min-height:0}.nUhMVa_src{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px;text-decoration:none}.nUhMVa_src:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.nUhMVa_dot{vertical-align:2px;margin-left:5px}.nUhMVa_act{flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px;font-size:11px;display:flex}.nUhMVa_actLive{color:var(--dsw-alias-state-success-primary,#16a34a);align-items:center;gap:4px;font-weight:600;display:inline-flex}.nUhMVa_actWarn{color:var(--dsw-alias-state-warn-primary,#b45309);align-items:center;gap:4px;font-weight:600;display:inline-flex}.nUhMVa_actBroken{color:var(--dsw-alias-state-error-primary,#dc2626);align-items:center;gap:4px;font-weight:600;display:inline-flex}.nUhMVa_actWhy{color:var(--dsw-alias-label-secondary,#6b7280);margin-top:2px}.nUhMVa_loading{color:var(--dsw-alias-label-secondary,#9ca3af);flex-direction:column;align-items:center;gap:12px;padding:48px;font-size:13px;display:flex}.nUhMVa_spin{color:var(--dsw-alias-brand-primary,#4f6ef7);flex-shrink:0;animation:.8s linear infinite nUhMVa_sp;display:inline-flex}.nUhMVa_logoMark{color:var(--dsw-alias-brand-primary,#4f6ef7);flex-shrink:0;display:inline-flex}.nUhMVa_logoPlug{transform-box:fill-box;transform-origin:50%;animation:1.5s cubic-bezier(.4,0,.2,1) infinite nUhMVa_dshmPlug}@keyframes nUhMVa_dshmPlug{0%,12%{transform:rotate(9deg)}45%,62%{transform:translate(-1.28px,1.27px)rotate(0)}95%,to{transform:rotate(9deg)}}@media (prefers-reduced-motion:reduce){.nUhMVa_logoPlug,.nUhMVa_spin{animation:1.5s ease-in-out infinite nUhMVa_dshmPlugFade}}@keyframes nUhMVa_dshmPlugFade{0%,to{opacity:1}50%{opacity:.35}}@keyframes nUhMVa_sp{to{transform:rotate(360deg)}}.nUhMVa_progress{background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:8px;flex-wrap:wrap;align-items:center;gap:9px;margin:0;padding:8px 12px;font-size:12px;display:flex}.nUhMVa_bar{background:var(--dsw-alias-border-l1,#e5e7eb);border-radius:99px;width:100%;height:4px;overflow:hidden}.nUhMVa_barFill{background:var(--dsw-alias-brand-primary,#4f6ef7);border-radius:99px;height:100%;transition:width .6s}.nUhMVa_barWave{width:30%;animation:1.2s ease-in-out infinite nUhMVa_dshmSlide}@keyframes nUhMVa_dshmSlide{0%{margin-left:-30%}to{margin-left:100%}}.nUhMVa_irow .nUhMVa_progress{margin-top:8px}.nUhMVa_progress code{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Menlo,monospace;font-size:11px;overflow:hidden}.nUhMVa_empty{color:var(--dsw-alias-label-secondary,#9ca3af);text-align:center;padding:32px;font-size:13px}.nUhMVa_err{color:var(--dsw-alias-state-error-primary,#dc2626);white-space:pre-wrap;word-break:break-all;margin:8px 0;font-size:12px}.nUhMVa_cardBlocked{border-color:var(--dsw-alias-state-error-primary,#dc2626)}.nUhMVa_conflictHead{align-items:flex-start;gap:8px;display:flex}.nUhMVa_conflictIcon{color:var(--dsw-alias-state-error-primary,#dc2626);flex-shrink:0;margin-top:1px}.nUhMVa_conflictTitle{color:var(--dsw-alias-state-error-primary,#dc2626);font-size:13px;font-weight:600;line-height:18px}.nUhMVa_conflictBody{margin:0;font-size:12px;line-height:19px}.nUhMVa_roster{background:var(--dsw-alias-border-l2,#e5e7eb);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;flex-direction:column;gap:1px;display:flex;overflow:hidden}.nUhMVa_rosterRow{background:var(--dsw-alias-bg-layer-1,#fff);align-items:center;gap:8px;padding:7px 10px;display:flex}.nUhMVa_rosterMain{flex-direction:column;min-width:0;display:flex}.nUhMVa_rosterName{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:12px;font-weight:600;overflow:hidden}.nUhMVa_rosterAuthor{color:var(--dsw-alias-label-secondary,#9ca3af);text-overflow:ellipsis;white-space:nowrap;font-size:10.5px;overflow:hidden}.nUhMVa_rosterTag{border-radius:4px;flex-shrink:0;margin-left:auto;padding:1px 6px;font-size:10.5px;font-weight:600}.nUhMVa_rosterTagKeep{color:var(--dsw-alias-state-success-primary,#16a34a);background:#16a34a1f}.nUhMVa_rosterTagDrop{color:var(--dsw-alias-state-error-primary,#dc2626);background:#dc26261f}.nUhMVa_rosterRowOut .nUhMVa_rosterName{text-decoration:line-through}.nUhMVa_rosterRowOut{opacity:.62}.nUhMVa_rosterSplit{background:var(--dsw-alias-border-l2,#e5e7eb);height:1px}.nUhMVa_reassure{color:var(--dsw-alias-label-secondary,#6b7280);align-items:center;gap:5px;margin:0;font-size:11.5px;line-height:17px;display:flex}.nUhMVa_reassureOk{color:var(--dsw-alias-state-success-primary,#16a34a);flex-shrink:0}.nUhMVa_conflictWhy{color:var(--dsw-alias-label-tertiary,#8b93a1);overflow-wrap:anywhere;margin-top:2px;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:17px}.nUhMVa_conflictWhyText{margin-top:5px;font-family:-apple-system,BlinkMacSystemFont,PingFang SC,sans-serif}.nUhMVa_choices{flex-direction:column;gap:7px;display:flex}.nUhMVa_choice{text-align:left;font:inherit;cursor:pointer;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l3,#d9dde3);border-radius:9px;align-items:flex-start;gap:9px;padding:9px 11px;display:flex}.nUhMVa_choice:has(input:disabled){cursor:default;opacity:.6}.nUhMVa_choiceOn{border-color:var(--dsw-alias-brand-primary,#4f6ef7);background:var(--dsw-alias-bg-layer-2,#f5f7ff)}.nUhMVa_choiceRadio{width:13px;height:13px;accent-color:var(--dsw-alias-brand-primary,#4f6ef7);flex-shrink:0;margin:2px 0 0}.nUhMVa_choiceMain{flex-direction:column;gap:3px;min-width:0;display:flex}.nUhMVa_choiceTitle{font-size:12px;font-weight:600;line-height:17px}.nUhMVa_choiceNote{color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:11px;line-height:16px}.nUhMVa_choiceSafe{color:var(--dsw-alias-state-success-primary,#16a34a)}.nUhMVa_stateTag{white-space:nowrap;background:var(--dsw-alias-bg-layer-2,#f3f4f6);min-height:20px;color:var(--dsw-alias-label-secondary,#6b7280);border-radius:5px;flex-shrink:0;align-items:center;gap:5px;padding:1px 7px;font-size:11px;line-height:16px;display:inline-flex}.nUhMVa_stateTag[data-on=true]{color:var(--dsw-alias-state-success-primary,#16a34a);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#16a34a) 10%, transparent)}.nUhMVa_stateDot{background:var(--dsw-alias-label-tertiary,#8b93a1);border-radius:999px;flex:none;width:6px;height:6px}.nUhMVa_stateDot[data-on=true]{background:var(--dsw-alias-state-success-primary,#16a34a)}.nUhMVa_metaTag{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-2,#f3f4f6);min-width:0;max-width:100%;min-height:20px;color:var(--dsw-alias-label-tertiary,#8b93a1);border-radius:5px;flex-shrink:1;padding:1px 7px;font-size:11px;line-height:18px;display:inline-block;overflow:hidden}.nUhMVa_metaTagOk{color:var(--dsw-alias-state-success-primary,#16a34a);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#16a34a) 10%, transparent)}.nUhMVa_nameLink{color:inherit;text-decoration:none}.nUhMVa_irowName{text-overflow:clip;flex-wrap:wrap;align-items:baseline;gap:6px;min-width:0;display:flex;overflow:visible}.nUhMVa_irowNameText{overflow-wrap:anywhere;white-space:normal;min-width:0}.nUhMVa_irowName>.nUhMVa_owner{flex:none}.nUhMVa_nameLink:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);text-decoration:underline}.nUhMVa_opWrap{flex-shrink:0;margin-bottom:6px;display:inline-flex;position:relative}.nUhMVa_opEntry{font:inherit;cursor:pointer;white-space:nowrap;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l3,#d9dde3);border-radius:7px;align-items:center;gap:6px;padding:4px 10px;font-size:12px;display:inline-flex;position:relative}.nUhMVa_opEntryQuiet{color:var(--dsw-alias-label-secondary,#6b7280);background:0 0;border-color:#0000}.nUhMVa_opEntryAlert{border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}.nUhMVa_opDot{background:var(--dsw-alias-state-error-primary,#dc2626);border-radius:99px;width:8px;height:8px;position:absolute;top:-3px;right:-3px}.nUhMVa_opPanel{z-index:40;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l3,#d9dde3);border-radius:12px;width:460px;max-width:86vw;max-height:70vh;position:absolute;top:calc(100% + 6px);right:0;overflow-y:auto;box-shadow:0 20px 52px #00000047}.nUhMVa_opHead{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:center;gap:8px;padding:9px 14px;display:flex}.nUhMVa_opPanelTitle{font-size:12.5px;font-weight:600}.nUhMVa_opCloseBtn.nUhMVa_opCloseBtn{min-width:0;color:var(--dsw-alias-label-secondary,#6b7280);padding:0 6px}.nUhMVa_opAggregate{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-2,#f7f8fa);padding:9px 14px}.nUhMVa_opAggregateTop{font-variant-numeric:tabular-nums;font-size:12px;font-weight:600}.nUhMVa_opAggregateHint{color:var(--dsw-alias-label-tertiary,#8b93a1);margin-top:4px;font-size:10.5px}.nUhMVa_opRow{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:flex-start;gap:9px;padding:9px 14px;display:flex}.nUhMVa_opRow:last-child{border-bottom:none}.nUhMVa_opRowAlert{background:#dc26260f}.nUhMVa_opIcon{flex-shrink:0;place-items:center;width:14px;margin-top:1px;display:grid}.nUhMVa_opQueuedIcon{color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:12px}.nUhMVa_opMain{flex-direction:column;flex:1;gap:3px;min-width:0;display:flex}.nUhMVa_opTop{align-items:baseline;gap:6px;min-width:0;display:flex}.nUhMVa_opVerb{color:var(--dsw-alias-label-secondary,#6b7280);flex-shrink:0;font-size:12px}.nUhMVa_opName{text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:600;overflow:hidden}.nUhMVa_opStatus{color:var(--dsw-alias-label-tertiary,#8b93a1);overflow-wrap:anywhere;font-size:10.5px;line-height:16px}.nUhMVa_opStatusBad{color:var(--dsw-alias-state-error-primary,#dc2626)}.nUhMVa_opActions{flex-shrink:0;align-items:center;gap:6px;margin-top:1px;display:flex}.nUhMVa_opDecision{border-top:1px dashed var(--dsw-alias-border-l2,#e5e7eb);flex-direction:column;gap:8px;margin-top:8px;padding-top:8px;display:flex}.nUhMVa_opDecisionFoot{align-items:center;gap:8px;display:flex}.nUhMVa_conflictDetailsToggle{font:inherit;cursor:pointer;color:var(--dsw-alias-label-tertiary,#8b93a1);background:0 0;border:none;align-items:center;gap:4px;padding:2px 0;font-size:11px;display:inline-flex}.nUhMVa_conflictDetailsToggle:hover{color:var(--dsw-alias-label-secondary,#6b7280)}.nUhMVa_opEmpty{text-align:center;color:var(--dsw-alias-label-secondary,#6b7280);padding:30px 14px;font-size:12px}.nUhMVa_opEmptyHint{color:var(--dsw-alias-label-tertiary,#8b93a1);margin-top:4px;font-size:11px}.nUhMVa_cardBlockedMark{font:inherit;cursor:pointer;color:var(--dsw-alias-state-error-primary,#dc2626);border:1px solid var(--dsw-alias-state-error-primary,#dc2626);background:#dc262614;border-radius:7px;align-items:center;gap:5px;padding:4px 9px;font-size:11px;display:inline-flex}.nUhMVa_dangerBtn.nUhMVa_dangerBtn{color:var(--dsw-alias-state-error-primary,#dc2626);border-color:var(--dsw-alias-state-error-primary,#dc2626)}.nUhMVa_dangerBtn.nUhMVa_dangerBtn:hover:not(:disabled){background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff}.nUhMVa_dangerArmed.nUhMVa_dangerArmed{background:var(--dsw-alias-state-error-primary,#dc2626);border-color:var(--dsw-alias-state-error-primary,#dc2626);color:#fff}.nUhMVa_retryBtn{margin-top:4px}.nUhMVa_irow{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:10px;flex-direction:column;gap:10px;min-width:0;padding:12px 14px;display:flex}.nUhMVa_irowActions{flex-wrap:wrap;justify-content:flex-start;align-items:center;gap:8px;min-width:0;display:flex}.nUhMVa_irowTrailing{flex-wrap:nowrap;align-items:center;gap:8px;min-width:0;display:inline-flex}.nUhMVa_irowMissing{filter:grayscale();opacity:.5}.nUhMVa_irow>.nUhMVa_src,.nUhMVa_irow>.nUhMVa_owner,.nUhMVa_irow button{white-space:nowrap;flex-shrink:0}.nUhMVa_tabSearchRow{padding:0 4px 6px;display:flex}.nUhMVa_tabSearch{width:100%}.nUhMVa_spec{color:var(--dsw-alias-label-secondary,#9ca3af);overflow-wrap:anywhere;min-width:0;font-family:ui-monospace,Menlo,monospace;font-size:11px}.nUhMVa_specTag{white-space:nowrap;border-radius:4px;flex-shrink:0;align-items:center;height:16px;padding:0 5px;font-size:10px;font-weight:600;display:inline-flex}.nUhMVa_specTagGit{color:#0b7285;background:#12a3c41f}.nUhMVa_specTagFile{color:#b07d1b;background:#f0b42924}.nUhMVa_backupCheckList .nUhMVa_grow{white-space:nowrap;text-overflow:ellipsis;flex:70%;min-width:0;overflow:hidden}.nUhMVa_backupCheckList .nUhMVa_spec{text-align:right;white-space:nowrap;text-overflow:ellipsis;flex:0 30%;max-width:30%;overflow:hidden}.nUhMVa_staleAction{margin-top:8px}.nUhMVa_pct{color:var(--dsw-alias-label-secondary,#6b7280);flex-shrink:0;font-size:11px;font-weight:600}.nUhMVa_pager{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin:16px 0 4px;display:flex}.nUhMVa_pagerPages{flex-wrap:wrap;flex:1;justify-content:center;align-items:center;gap:6px;min-width:0;display:flex}.nUhMVa_pageEllipsis{color:var(--dsw-alias-label-secondary,#9ca3af);padding:0 2px;font-size:12px}.nUhMVa_pageInfo{color:var(--dsw-alias-label-secondary,#6b7280);white-space:nowrap;font-size:12px}.nUhMVa_depBadge{border:1px solid var(--dsw-alias-state-warn-primary,#b45309);color:var(--dsw-alias-state-warn-primary,#b45309);white-space:nowrap;border-radius:4px;flex-shrink:0;margin-left:6px;padding:1px 6px;font-size:11px;font-weight:600;line-height:16px}.nUhMVa_deprecate{color:var(--dsw-alias-state-warn-primary,#b45309);background:var(--dsw-alias-bg-layer-2,#fdf3e3);border:1px solid var(--dsw-alias-border-l2,#f3e3c3);border-radius:8px;margin:0;padding:8px 10px;font-size:12px;line-height:18px}.nUhMVa_deprecate a{color:var(--dsw-alias-state-warn-primary,#b45309);text-decoration:underline}.nUhMVa_deprecate .nUhMVa_src{margin-left:8px}.nUhMVa_depLine{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.nUhMVa_switch{border:1px solid var(--dsw-alias-border-l2,#d9dde3);background:var(--dsw-alias-bg-layer-2,#e5e7eb);cursor:pointer;border-radius:99px;flex-shrink:0;width:38px;height:22px;padding:0;transition:background .15s,border-color .15s;position:relative}.nUhMVa_switchOn{background:var(--dsw-alias-state-success-primary,#16a34a);border-color:var(--dsw-alias-state-success-primary,#16a34a)}.nUhMVa_switchMixed{background:var(--dsw-alias-state-warn-primary,#b45309);border-color:var(--dsw-alias-state-warn-primary,#b45309)}.nUhMVa_switchKnob{background:#fff;border-radius:99px;width:16px;height:16px;transition:left .15s;position:absolute;top:2px;left:2px;box-shadow:0 1px 2px #00000040}.nUhMVa_switchOn .nUhMVa_switchKnob,.nUhMVa_switchMixed .nUhMVa_switchKnob{left:18px}.nUhMVa_switch:disabled{opacity:.5;cursor:default}.nUhMVa_viewBar{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;align-items:center;gap:2px;width:fit-content;margin-bottom:12px;padding:2px;display:flex}.nUhMVa_viewBtn{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:6px;padding:4px 10px;font-size:12px;line-height:18px}.nUhMVa_viewBtn:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.nUhMVa_viewOn{background:var(--dsw-alias-bg-layer-2,#eef0f4);color:var(--dsw-alias-label-primary,#1f2328);font-weight:600}.nUhMVa_groupRow{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;margin-bottom:10px;padding:12px 14px}.nUhMVa_groupHead{align-items:center;gap:10px;min-width:0;display:flex}.nUhMVa_groupName{text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;line-height:20px;overflow:hidden}.nUhMVa_groupActions{flex-shrink:0;align-items:center;gap:6px;display:flex}.nUhMVa_groupMembers{flex-direction:column;gap:6px;margin-top:10px;display:flex}.nUhMVa_groupMember{background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-radius:8px;align-items:center;gap:8px;padding:6px 8px;font-size:12px;line-height:18px;display:flex}.nUhMVa_groupMember .nUhMVa_nm{flex:1;min-width:0;font-size:12px}.nUhMVa_groupAddPanel{border-top:1px dashed var(--dsw-alias-border-l2,#e5e7eb);flex-direction:column;gap:6px;margin-top:10px;padding-top:10px;display:flex}.nUhMVa_groupCreate{align-items:center;gap:8px;margin-bottom:10px;display:flex}.nUhMVa_inlineInput{flex:1;min-width:120px}.nUhMVa_assignRow{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.nUhMVa_assignSelect{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font:inherit;border-radius:6px;padding:3px 6px;font-size:12px;line-height:18px}.nUhMVa_groupHint{color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:11px}.nUhMVa_sectAction{color:var(--dsw-alias-label-secondary,#6b7280);align-items:center;gap:8px;margin:14px 2px 8px;font-size:12px;font-weight:600;display:flex}.nUhMVa_backupGrid{grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px;display:grid}.nUhMVa_backupCard{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-direction:column;gap:10px;padding:16px;display:flex}.nUhMVa_backupCard h3{margin:0;font-size:14px}.nUhMVa_backupCard p{color:var(--dsw-alias-label-secondary,#6b7280);margin:0;font-size:12px;line-height:18px}.nUhMVa_backupActions{flex-wrap:wrap;gap:8px;display:flex;position:relative}.nUhMVa_hiddenFile{opacity:0;pointer-events:none;width:1px;height:1px;position:absolute}.nUhMVa_backupInput{box-sizing:border-box;width:100%}.nUhMVa_backupCheck{cursor:pointer;align-items:center;gap:6px;font-size:12px;display:flex}.nUhMVa_backupWarn{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-state-warn-primary,#b45309)!important}.nUhMVa_backupMessage{color:var(--dsw-alias-label-secondary,#6b7280);grid-column:1/-1;font-size:12px}.nUhMVa_backupCheckList{flex-direction:column;gap:6px;max-height:260px;margin:10px 0 4px;padding-right:4px;display:flex;overflow-y:auto}.nUhMVa_backupCheckList .nUhMVa_backupCheck{justify-content:space-between;gap:8px}.nUhMVa_diagPage{flex-direction:column;gap:12px;height:100%;min-height:0;display:flex;overflow-y:auto}.nUhMVa_diagSummary{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-wrap:wrap;align-items:center;gap:12px;padding:10px 14px;font-size:12px;display:flex}.nUhMVa_diagSummaryItem{color:var(--dsw-alias-label-secondary,#6b7280);white-space:nowrap;align-items:center;gap:6px;display:inline-flex}.nUhMVa_diagSummaryMeta{color:var(--dsw-alias-label-tertiary,#9ca3af);text-overflow:ellipsis;white-space:nowrap;max-width:320px;font-family:ui-monospace,Menlo,monospace;font-size:11px;overflow:hidden}.nUhMVa_diagSection{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-direction:column;gap:8px;padding:12px 14px;display:flex}.nUhMVa_diagSection h3{color:var(--dsw-alias-label-primary,#1f2328);margin:0;font-size:13px;font-weight:600}.nUhMVa_diagCount{color:var(--dsw-alias-label-tertiary,#9ca3af);font-size:11px;font-weight:400}.nUhMVa_diagEmpty{color:var(--dsw-alias-label-secondary,#9ca3af);padding:8px 0;font-size:12px}.nUhMVa_diagBundle{border-top:1px solid var(--dsw-alias-border-l2,#f0f1f3);flex-direction:column;gap:6px;padding-top:8px;display:flex}.nUhMVa_diagBundle:first-of-type{border-top:none;padding-top:0}.nUhMVa_diagRow{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;font-size:12px;line-height:18px;display:flex}.nUhMVa_diagMeta{align-items:baseline;gap:8px;min-width:0;font-size:12px;display:flex}.nUhMVa_diagKey{color:var(--dsw-alias-label-tertiary,#9ca3af);flex-shrink:0;min-width:64px;font-size:11px}.nUhMVa_diagVal{color:var(--dsw-alias-label-primary,#1f2328);overflow-wrap:anywhere;min-width:0;font-family:ui-monospace,Menlo,monospace;font-size:12px;font-weight:500}.nUhMVa_diagIndex{background:var(--dsw-alias-bg-layer-2,#f3f4f6);min-width:18px;height:18px;color:var(--dsw-alias-label-secondary,#6b7280);border-radius:9px;flex-shrink:0;justify-content:center;align-items:center;font-size:11px;font-weight:600;display:inline-flex}.nUhMVa_diagArrow{color:var(--dsw-alias-label-tertiary,#9ca3af);flex-shrink:0;font-size:12px}.nUhMVa_diagBadgeOfficial{background:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff;border-radius:9px;flex-shrink:0;align-items:center;height:18px;padding:0 8px;font-size:11px;font-weight:600;display:inline-flex}.nUhMVa_diagBadgeCommunity{background:var(--dsw-alias-bg-layer-2,#f3f4f6);height:18px;color:var(--dsw-alias-label-secondary,#6b7280);border-radius:9px;flex-shrink:0;align-items:center;padding:0 8px;font-size:11px;display:inline-flex}.nUhMVa_diagBadgeShadow{background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff;border-radius:9px;flex-shrink:0;align-items:center;height:18px;padding:0 8px;font-size:11px;font-weight:600;display:inline-flex}.nUhMVa_diagBadgeWarn{background:var(--dsw-alias-state-warn-primary,#b45309);color:#fff;white-space:nowrap;border-radius:9px;flex-shrink:0;align-items:center;height:18px;padding:0 8px;font-size:11px;font-weight:600;display:inline-flex}.nUhMVa_diagBadgeInfo{background:var(--dsw-alias-bg-layer-2,#f3f4f6);height:18px;color:var(--dsw-alias-label-secondary,#6b7280);white-space:nowrap;border-radius:9px;flex-shrink:0;align-items:center;padding:0 8px;font-size:11px;display:inline-flex}.nUhMVa_diagList{flex-direction:column;gap:6px;display:flex}.nUhMVa_sectionOverview{color:var(--dsw-alias-label-tertiary,#8b93a1);text-overflow:ellipsis;white-space:nowrap;max-width:100%;padding:2px 0 6px;font-size:12px;line-height:18px;overflow:hidden}.nUhMVa_diagAlert{color:var(--dsw-alias-state-warn-primary,#b45309)}.nUhMVa_ovRow{background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-radius:8px;flex-wrap:wrap;align-items:center;gap:8px;min-width:0;padding:6px 10px;font-size:12px;line-height:18px;display:flex}.nUhMVa_ovArrow{color:var(--dsw-alias-label-tertiary,#9ca3af);flex-shrink:0;font-size:12px}.nUhMVa_ovByTag{background:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff;text-overflow:ellipsis;white-space:nowrap;border-radius:9px;flex-shrink:0;align-items:center;max-width:260px;height:18px;padding:0 8px;font-size:11px;font-weight:600;display:inline-flex;overflow:hidden}.nUhMVa_ovFrom{color:var(--dsw-alias-label-secondary,#6b7280);text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:12px;overflow:hidden}.nUhMVa_orphRow{background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-radius:8px;flex-wrap:wrap;align-items:center;gap:8px;min-width:0;padding:6px 10px;font-size:12px;line-height:18px;display:flex}.nUhMVa_orphBadge{background:var(--dsw-alias-state-warn-primary,#b45309);color:#fff;white-space:nowrap;border-radius:9px;flex-shrink:0;align-items:center;height:18px;padding:0 8px;font-size:11px;font-weight:600;display:inline-flex}.nUhMVa_dragHandle{width:20px;height:20px;color:var(--dsw-alias-label-tertiary,#9ca3af);cursor:grab;-webkit-user-select:none;user-select:none;flex-shrink:0;justify-content:center;align-items:center;font-size:12px;line-height:20px;display:inline-flex}.nUhMVa_dragOver{outline:2px dashed var(--dsw-alias-brand-primary,#4f6ef7);outline-offset:2px;background:var(--dsw-alias-bg-layer-2,#f0f2f8);border-radius:8px}.nUhMVa_dragging{opacity:.45;background:var(--dsw-alias-bg-layer-2,#f3f4f6)}.nUhMVa_collapseHead{font:inherit;color:var(--dsw-alias-label-primary,#1f2328);cursor:pointer;text-align:left;background:0 0;border:none;align-items:center;gap:8px;width:100%;padding:0;font-size:13px;font-weight:600;display:flex}.nUhMVa_collapseIcon{color:var(--dsw-alias-label-secondary,#6b7280);flex-shrink:0;display:inline-flex}.nUhMVa_collapseTitle{flex:1;min-width:0}.nUhMVa_collapseBody{border-top:1px solid var(--dsw-alias-border-l2,#f0f1f3);overflow-wrap:anywhere;flex-direction:column;gap:10px;min-width:0;margin-top:8px;padding-top:10px;display:flex}.nUhMVa_orderPanel{flex-direction:column;gap:10px;min-width:0;display:flex}.nUhMVa_panelNote{color:var(--dsw-alias-label-secondary,#6b7280);margin:0;font-size:12px;line-height:18px}.nUhMVa_panelActions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.nUhMVa_presetList{flex-direction:column;gap:6px;min-width:0;display:flex}.nUhMVa_presetRow{background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-radius:8px;flex-direction:column;align-items:stretch;gap:6px;min-width:0;max-width:100%;padding:8px 10px;font-size:12px;line-height:18px;display:flex}.nUhMVa_presetName{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:13px;font-weight:600;overflow:hidden}.nUhMVa_snapList{flex-direction:column;gap:6px;min-width:0;display:flex}.nUhMVa_snapRow{background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-radius:8px;flex-direction:column;align-items:stretch;gap:8px;padding:10px 12px;font-size:12px;line-height:18px;display:flex}.nUhMVa_snapMeta{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;display:flex}.nUhMVa_snapConfirmText{color:var(--dsw-alias-state-warn-primary,#b45309);margin:0;font-size:12px;line-height:18px}.nUhMVa_confirmRow{flex-wrap:wrap;align-items:center;gap:6px;display:inline-flex}.nUhMVa_fixFallback{flex-direction:column;gap:6px;margin:8px 0;display:flex}.nUhMVa_fixFallbackText{box-sizing:border-box;width:100%;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);resize:vertical;white-space:pre-wrap;word-break:break-all;border-radius:6px;padding:8px 10px;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:16px}.nUhMVa_setCard{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-3,#fff);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.nUhMVa_setCard:hover{border-color:var(--dsw-alias-label-dimmed,#c8ccd4)}.nUhMVa_setCardOpen{background:var(--dsw-alias-bg-layer-2,#f7f8fa);border-color:var(--dsw-alias-label-dimmed,#c8ccd4)}.nUhMVa_setHeader{-webkit-appearance:none;appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.nUhMVa_setHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4f6ef7);outline-offset:-2px}.nUhMVa_setHeadText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.nUhMVa_setName{color:var(--dsw-alias-label-primary,#1f2328);font-size:15px;font-weight:600;line-height:1.4}.nUhMVa_setDesc{color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:13px;line-height:1.5}.nUhMVa_setChevron{color:var(--dsw-alias-label-tertiary,#8b93a1);flex:none;transition:transform .16s;display:inline-flex}.nUhMVa_setChevronOpen{transform:rotate(180deg)}.nUhMVa_setBody{border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);margin:0 16px;padding-bottom:8px}.nUhMVa_setRow{align-items:center;gap:12px;padding:12px 0;display:flex}.nUhMVa_setRow+.nUhMVa_setRow{border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb)}.nUhMVa_setLabelBox{flex-direction:column;flex:1;gap:3px;min-width:0;display:flex}.nUhMVa_setLabel{font-size:13px;line-height:20px}.nUhMVa_setHint{color:var(--dsw-alias-label-tertiary,#8b93a1);font-size:12px;line-height:18px}.nUhMVa_setConfirm{border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);flex-direction:column;gap:8px;padding:12px 0 4px;display:flex}.nUhMVa_setCheck{cursor:pointer;align-items:center;gap:8px;font-size:12px;line-height:18px;display:flex}.nUhMVa_setActions{border-top:1px solid var(--dsw-alias-border-l2,#e5e7eb);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.nUhMVa_setDanger{color:var(--dsw-alias-state-error-primary,#dc2626)}.nUhMVa_setSeg{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;flex-shrink:0;gap:2px;padding:2px;display:inline-flex}.nUhMVa_setSegBtn{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;background:0 0;border:none;border-radius:6px;padding:3px 10px;font-size:12px;line-height:18px}.nUhMVa_setSegBtn:disabled{cursor:default;opacity:.5}.nUhMVa_setSegOn{background:var(--dsw-alias-bg-layer-2,#eef0f4);color:var(--dsw-alias-label-primary,#1f2328);font-weight:600}.nUhMVa_setBetaTag{background:var(--dsw-alias-bg-module-platform,#eef0f4);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:9px;margin-left:6px;padding:0 6px;font-size:11px;font-weight:600;line-height:17px}.nUhMVa_commentsLink{cursor:pointer;white-space:nowrap;color:var(--dsw-alias-label-secondary,#9ca3af);background:0 0;border:0;padding:0;font-size:11px;line-height:16px}.nUhMVa_commentsLink:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.nUhMVa_commentsNote{color:var(--dsw-alias-label-secondary,#9ca3af);margin:0 0 8px;font-size:11px;line-height:16px}.nUhMVa_commentsStatus{color:var(--dsw-alias-label-secondary,#9ca3af);margin:0;font-size:12px}.nUhMVa_commentsStatus:empty{display:none}.nUhMVa_commentsError{color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 8px;font-size:12px}.nUhMVa_commentsFail{flex-direction:column;align-items:flex-start;gap:8px;padding:12px 0;display:flex}.nUhMVa_commentsMount{width:100%;min-height:240px;max-height:56vh;overflow:auto}.nUhMVa_commentsMount iframe{width:100%}.nUhMVa_notesLink{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;text-align:left;background:0 0;border:none;margin:2px 0 0;padding:0;font-size:12px;line-height:18px;display:block}.nUhMVa_notesLink:hover{color:var(--dsw-alias-label-primary,#1f2328)}.nUhMVa_notesBody{flex-direction:column;gap:8px;min-width:0;display:flex}.nUhMVa_notesRange{align-items:center;gap:8px;margin-bottom:4px;font-size:12px;line-height:18px;display:flex}.nUhMVa_notesArrow{color:var(--dsw-alias-label-tertiary,#8b93a1)}.nUhMVa_notesMeta{letter-spacing:.02em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#6b7280);flex-wrap:wrap;align-items:baseline;gap:6px;margin-top:12px;font-size:12px;line-height:18px;display:flex}.nUhMVa_notesPre{white-space:pre-wrap;word-break:break-word;background:var(--dsw-alias-bg-layer-2,#eef0f4);border-radius:8px;max-height:40vh;margin:0;padding:10px 12px;font-size:13px;line-height:20px;overflow:auto}.nUhMVa_notesList{flex-direction:column;gap:6px;margin:0;padding-left:18px;font-size:13px;line-height:20px;display:flex}.nUhMVa_notesRendered{flex-direction:column;gap:6px;min-width:0;display:flex}.nUhMVa_notesH{margin-top:6px;font-size:13px;font-weight:650;line-height:20px}.nUhMVa_notesP{font-size:13px;line-height:20px}.nUhMVa_notesCode{font-family:var(--dsw-alias-font-mono,ui-monospace,monospace);background:var(--dsw-alias-bg-layer-2,#eef0f4);border-radius:5px;padding:1px 5px;font-size:12px}.nUhMVa_notesList{flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.nUhMVa_notesRow{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:baseline;gap:12px;padding:6px 0;display:flex}.nUhMVa_notesRow:last-child{border-bottom:none}.nUhMVa_notesDate{color:var(--dsw-alias-label-tertiary,#8b93a1);font-variant-numeric:tabular-nums;flex-shrink:0;font-size:12px}.nUhMVa_notesMsg{word-break:break-word;word-break:break-word;-webkit-line-clamp:3;-webkit-box-orient:vertical;min-width:0;font-size:13px;line-height:20px;display:-webkit-box;overflow:hidden}.nUhMVa_notesSha{color:var(--dsw-alias-label-secondary,#6b7280);font-variant-numeric:tabular-nums;flex-shrink:0;margin-left:auto;font-size:12px;text-decoration:none}.nUhMVa_notesSha:hover{color:var(--dsw-alias-label-primary,#1f2328);text-decoration:underline}.nUhMVa_notesVer{color:inherit;font-weight:600;text-decoration:none}.nUhMVa_notesVer:hover{text-decoration:underline}"; + const tagId = "dshmarket/Market.module.css"; + if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) { + const tag = document.createElement("style"); + tag.dataset.plugin = "dshmarket"; + tag.dataset.pluginCss = tagId; + tag.textContent = css; + document.head.appendChild(tag); + } + var Market_module_css_default = { + "act": "nUhMVa_act", + "actBroken": "nUhMVa_actBroken", + "actLive": "nUhMVa_actLive", + "actWarn": "nUhMVa_actWarn", + "actWhy": "nUhMVa_actWhy", + "assignRow": "nUhMVa_assignRow", + "assignSelect": "nUhMVa_assignSelect", + "av": "nUhMVa_av", + "backupActions": "nUhMVa_backupActions", + "backupCard": "nUhMVa_backupCard", + "backupCheck": "nUhMVa_backupCheck", + "backupCheckList": "nUhMVa_backupCheckList", + "backupGrid": "nUhMVa_backupGrid", + "backupInput": "nUhMVa_backupInput", + "backupMessage": "nUhMVa_backupMessage", + "backupWarn": "nUhMVa_backupWarn", + "banner": "nUhMVa_banner", + "bannerHint": "nUhMVa_bannerHint", + "bannerIcon": "nUhMVa_bannerIcon", + "bar": "nUhMVa_bar", + "barFill": "nUhMVa_barFill", + "barWave": "nUhMVa_barWave", + "body": "nUhMVa_body", + "byline": "nUhMVa_byline", + "card": "nUhMVa_card", + "cardAction": "nUhMVa_cardAction", + "cardBlocked": "nUhMVa_cardBlocked", + "cardBlockedMark": "nUhMVa_cardBlockedMark", + "cardShot": "nUhMVa_cardShot", + "cardShots": "nUhMVa_cardShots", + "cats": "nUhMVa_cats", + "catsCollapsed": "nUhMVa_catsCollapsed", + "catsRow": "nUhMVa_catsRow", + "catsToggle": "nUhMVa_catsToggle", + "catsWrap": "nUhMVa_catsWrap", + "choice": "nUhMVa_choice", + "choiceMain": "nUhMVa_choiceMain", + "choiceNote": "nUhMVa_choiceNote", + "choiceOn": "nUhMVa_choiceOn", + "choiceRadio": "nUhMVa_choiceRadio", + "choices": "nUhMVa_choices", + "choiceSafe": "nUhMVa_choiceSafe", + "choiceTitle": "nUhMVa_choiceTitle", + "cmd": "nUhMVa_cmd", + "collapseBody": "nUhMVa_collapseBody", + "collapseHead": "nUhMVa_collapseHead", + "collapseIcon": "nUhMVa_collapseIcon", + "collapseTitle": "nUhMVa_collapseTitle", + "commentsError": "nUhMVa_commentsError", + "commentsFail": "nUhMVa_commentsFail", + "commentsLink": "nUhMVa_commentsLink", + "commentsMount": "nUhMVa_commentsMount", + "commentsNote": "nUhMVa_commentsNote", + "commentsStatus": "nUhMVa_commentsStatus", + "confirmRow": "nUhMVa_confirmRow", + "conflictBody": "nUhMVa_conflictBody", + "conflictDetailsToggle": "nUhMVa_conflictDetailsToggle", + "conflictHead": "nUhMVa_conflictHead", + "conflictIcon": "nUhMVa_conflictIcon", + "conflictTitle": "nUhMVa_conflictTitle", + "conflictWhy": "nUhMVa_conflictWhy", + "conflictWhyText": "nUhMVa_conflictWhyText", + "dangerArmed": "nUhMVa_dangerArmed", + "dangerBtn": "nUhMVa_dangerBtn", + "depBadge": "nUhMVa_depBadge", + "depLine": "nUhMVa_depLine", + "deprecate": "nUhMVa_deprecate", + "desc": "nUhMVa_desc", + "descClamp": "nUhMVa_descClamp", + "descTight": "nUhMVa_descTight", + "descToggle": "nUhMVa_descToggle", + "diagAlert": "nUhMVa_diagAlert", + "diagArrow": "nUhMVa_diagArrow", + "diagBadgeCommunity": "nUhMVa_diagBadgeCommunity", + "diagBadgeInfo": "nUhMVa_diagBadgeInfo", + "diagBadgeOfficial": "nUhMVa_diagBadgeOfficial", + "diagBadgeShadow": "nUhMVa_diagBadgeShadow", + "diagBadgeWarn": "nUhMVa_diagBadgeWarn", + "diagBundle": "nUhMVa_diagBundle", + "diagCount": "nUhMVa_diagCount", + "diagEmpty": "nUhMVa_diagEmpty", + "diagIndex": "nUhMVa_diagIndex", + "diagKey": "nUhMVa_diagKey", + "diagList": "nUhMVa_diagList", + "diagMeta": "nUhMVa_diagMeta", + "diagPage": "nUhMVa_diagPage", + "diagRow": "nUhMVa_diagRow", + "diagSection": "nUhMVa_diagSection", + "diagSummary": "nUhMVa_diagSummary", + "diagSummaryItem": "nUhMVa_diagSummaryItem", + "diagSummaryMeta": "nUhMVa_diagSummaryMeta", + "diagVal": "nUhMVa_diagVal", + "dot": "nUhMVa_dot", + "dragging": "nUhMVa_dragging", + "dragHandle": "nUhMVa_dragHandle", + "dragOver": "nUhMVa_dragOver", + "dshmPlug": "nUhMVa_dshmPlug", + "dshmPlugFade": "nUhMVa_dshmPlugFade", + "dshmSlide": "nUhMVa_dshmSlide", + "empty": "nUhMVa_empty", + "err": "nUhMVa_err", + "exportLogBtn": "nUhMVa_exportLogBtn", + "fixFallback": "nUhMVa_fixFallback", + "fixFallbackText": "nUhMVa_fixFallbackText", + "foot": "nUhMVa_foot", + "grid": "nUhMVa_grid", + "groupActions": "nUhMVa_groupActions", + "groupAddPanel": "nUhMVa_groupAddPanel", + "groupCreate": "nUhMVa_groupCreate", + "groupHead": "nUhMVa_groupHead", + "groupHint": "nUhMVa_groupHint", + "groupMember": "nUhMVa_groupMember", + "groupMembers": "nUhMVa_groupMembers", + "groupName": "nUhMVa_groupName", + "groupRow": "nUhMVa_groupRow", + "grow": "nUhMVa_grow", + "head": "nUhMVa_head", + "hiddenFile": "nUhMVa_hiddenFile", + "inlineInput": "nUhMVa_inlineInput", + "installBtn": "nUhMVa_installBtn", + "irow": "nUhMVa_irow", + "irowActions": "nUhMVa_irowActions", + "irowMissing": "nUhMVa_irowMissing", + "irowName": "nUhMVa_irowName", + "irowNameText": "nUhMVa_irowNameText", + "irowTrailing": "nUhMVa_irowTrailing", + "lightbox": "nUhMVa_lightbox", + "lightboxClose": "nUhMVa_lightboxClose", + "lightboxDot": "nUhMVa_lightboxDot", + "lightboxDotOn": "nUhMVa_lightboxDotOn", + "lightboxDots": "nUhMVa_lightboxDots", + "lightboxImg": "nUhMVa_lightboxImg", + "lightboxNav": "nUhMVa_lightboxNav", + "lightboxNext": "nUhMVa_lightboxNext", + "lightboxPrev": "nUhMVa_lightboxPrev", + "loading": "nUhMVa_loading", + "logoMark": "nUhMVa_logoMark", + "logoPlug": "nUhMVa_logoPlug", + "masonry": "nUhMVa_masonry", + "masonryCol": "nUhMVa_masonryCol", + "meta": "nUhMVa_meta", + "metaInline": "nUhMVa_metaInline", + "metaTag": "nUhMVa_metaTag", + "metaTagOk": "nUhMVa_metaTagOk", + "modalNote": "nUhMVa_modalNote", + "nameLink": "nUhMVa_nameLink", + "nm": "nUhMVa_nm", + "nmLink": "nUhMVa_nmLink", + "noteAction": "nUhMVa_noteAction", + "noteEdit": "nUhMVa_noteEdit", + "noteInput": "nUhMVa_noteInput", + "noteMine": "nUhMVa_noteMine", + "noteRow": "nUhMVa_noteRow", + "notesArrow": "nUhMVa_notesArrow", + "notesBody": "nUhMVa_notesBody", + "notesCode": "nUhMVa_notesCode", + "notesDate": "nUhMVa_notesDate", + "notesH": "nUhMVa_notesH", + "notesLink": "nUhMVa_notesLink", + "notesList": "nUhMVa_notesList", + "notesMeta": "nUhMVa_notesMeta", + "notesMsg": "nUhMVa_notesMsg", + "notesP": "nUhMVa_notesP", + "notesPre": "nUhMVa_notesPre", + "notesRange": "nUhMVa_notesRange", + "notesRendered": "nUhMVa_notesRendered", + "notesRow": "nUhMVa_notesRow", + "notesSha": "nUhMVa_notesSha", + "notesVer": "nUhMVa_notesVer", + "noteToggle": "nUhMVa_noteToggle", + "okState": "nUhMVa_okState", + "on": "nUhMVa_on", + "opActions": "nUhMVa_opActions", + "opAggregate": "nUhMVa_opAggregate", + "opAggregateHint": "nUhMVa_opAggregateHint", + "opAggregateTop": "nUhMVa_opAggregateTop", + "opCloseBtn": "nUhMVa_opCloseBtn", + "opDecision": "nUhMVa_opDecision", + "opDecisionFoot": "nUhMVa_opDecisionFoot", + "opDot": "nUhMVa_opDot", + "opEmpty": "nUhMVa_opEmpty", + "opEmptyHint": "nUhMVa_opEmptyHint", + "opEntry": "nUhMVa_opEntry", + "opEntryAlert": "nUhMVa_opEntryAlert", + "opEntryQuiet": "nUhMVa_opEntryQuiet", + "opHead": "nUhMVa_opHead", + "opIcon": "nUhMVa_opIcon", + "opMain": "nUhMVa_opMain", + "opName": "nUhMVa_opName", + "opPanel": "nUhMVa_opPanel", + "opPanelTitle": "nUhMVa_opPanelTitle", + "opQueuedIcon": "nUhMVa_opQueuedIcon", + "opRow": "nUhMVa_opRow", + "opRowAlert": "nUhMVa_opRowAlert", + "opStatus": "nUhMVa_opStatus", + "opStatusBad": "nUhMVa_opStatusBad", + "opTop": "nUhMVa_opTop", + "opVerb": "nUhMVa_opVerb", + "opWrap": "nUhMVa_opWrap", + "orderPanel": "nUhMVa_orderPanel", + "orphBadge": "nUhMVa_orphBadge", + "orphRow": "nUhMVa_orphRow", + "ovArrow": "nUhMVa_ovArrow", + "ovByTag": "nUhMVa_ovByTag", + "ovFrom": "nUhMVa_ovFrom", + "ovRow": "nUhMVa_ovRow", + "owner": "nUhMVa_owner", + "pageEllipsis": "nUhMVa_pageEllipsis", + "pageInfo": "nUhMVa_pageInfo", + "pager": "nUhMVa_pager", + "pagerPages": "nUhMVa_pagerPages", + "panelActions": "nUhMVa_panelActions", + "panelNote": "nUhMVa_panelNote", + "pct": "nUhMVa_pct", + "presetList": "nUhMVa_presetList", + "presetName": "nUhMVa_presetName", + "presetRow": "nUhMVa_presetRow", + "progress": "nUhMVa_progress", + "reassure": "nUhMVa_reassure", + "reassureOk": "nUhMVa_reassureOk", + "repoLink": "nUhMVa_repoLink", + "repoMark": "nUhMVa_repoMark", + "retryBtn": "nUhMVa_retryBtn", + "root": "nUhMVa_root", + "roster": "nUhMVa_roster", + "rosterAuthor": "nUhMVa_rosterAuthor", + "rosterMain": "nUhMVa_rosterMain", + "rosterName": "nUhMVa_rosterName", + "rosterRow": "nUhMVa_rosterRow", + "rosterRowOut": "nUhMVa_rosterRowOut", + "rosterSplit": "nUhMVa_rosterSplit", + "rosterTag": "nUhMVa_rosterTag", + "rosterTagDrop": "nUhMVa_rosterTagDrop", + "rosterTagKeep": "nUhMVa_rosterTagKeep", + "row1": "nUhMVa_row1", + "sectAction": "nUhMVa_sectAction", + "sectionOverview": "nUhMVa_sectionOverview", + "setActions": "nUhMVa_setActions", + "setBetaTag": "nUhMVa_setBetaTag", + "setBody": "nUhMVa_setBody", + "setCard": "nUhMVa_setCard", + "setCardOpen": "nUhMVa_setCardOpen", + "setCheck": "nUhMVa_setCheck", + "setChevron": "nUhMVa_setChevron", + "setChevronOpen": "nUhMVa_setChevronOpen", + "setConfirm": "nUhMVa_setConfirm", + "setDanger": "nUhMVa_setDanger", + "setDesc": "nUhMVa_setDesc", + "setHeader": "nUhMVa_setHeader", + "setHeadText": "nUhMVa_setHeadText", + "setHint": "nUhMVa_setHint", + "setLabel": "nUhMVa_setLabel", + "setLabelBox": "nUhMVa_setLabelBox", + "setName": "nUhMVa_setName", + "setRow": "nUhMVa_setRow", + "setSeg": "nUhMVa_setSeg", + "setSegBtn": "nUhMVa_setSegBtn", + "setSegOn": "nUhMVa_setSegOn", + "shot": "nUhMVa_shot", + "shots": "nUhMVa_shots", + "snapConfirmText": "nUhMVa_snapConfirmText", + "snapList": "nUhMVa_snapList", + "snapMeta": "nUhMVa_snapMeta", + "snapRow": "nUhMVa_snapRow", + "sp": "nUhMVa_sp", + "spec": "nUhMVa_spec", + "specTag": "nUhMVa_specTag", + "specTagFile": "nUhMVa_specTagFile", + "specTagGit": "nUhMVa_specTagGit", + "spin": "nUhMVa_spin", + "src": "nUhMVa_src", + "staleAction": "nUhMVa_staleAction", + "star": "nUhMVa_star", + "stateDot": "nUhMVa_stateDot", + "stateTag": "nUhMVa_stateTag", + "stickyHead": "nUhMVa_stickyHead", + "sub": "nUhMVa_sub", + "submitLink": "nUhMVa_submitLink", + "subTabs": "nUhMVa_subTabs", + "swatches": "nUhMVa_swatches", + "switch": "nUhMVa_switch", + "switchKnob": "nUhMVa_switchKnob", + "switchMixed": "nUhMVa_switchMixed", + "switchOn": "nUhMVa_switchOn", + "tab": "nUhMVa_tab", + "tabs": "nUhMVa_tabs", + "tabSearch": "nUhMVa_tabSearch", + "tabSearchRow": "nUhMVa_tabSearchRow", + "tag": "nUhMVa_tag", + "themeActions": "nUhMVa_themeActions", + "themeCard": "nUhMVa_themeCard", + "themeCardBody": "nUhMVa_themeCardBody", + "themeCardFooter": "nUhMVa_themeCardFooter", + "themeCardHead": "nUhMVa_themeCardHead", + "themeCover": "nUhMVa_themeCover", + "themeCoverEmpty": "nUhMVa_themeCoverEmpty", + "themeDescription": "nUhMVa_themeDescription", + "themeFullscreenBtn": "nUhMVa_themeFullscreenBtn", + "themeGallery": "nUhMVa_themeGallery", + "themeIdentity": "nUhMVa_themeIdentity", + "themeLifecycle": "nUhMVa_themeLifecycle", + "themePreviewAction": "nUhMVa_themePreviewAction", + "themePreviewCount": "nUhMVa_themePreviewCount", + "themeResultBar": "nUhMVa_themeResultBar", + "themeSearch": "nUhMVa_themeSearch", + "themesGrid": "nUhMVa_themesGrid", + "themeStatus": "nUhMVa_themeStatus", + "themeStatusMuted": "nUhMVa_themeStatusMuted", + "themeToolbar": "nUhMVa_themeToolbar", + "themeToolbarActions": "nUhMVa_themeToolbarActions", + "title": "nUhMVa_title", + "titleRow": "nUhMVa_titleRow", + "top": "nUhMVa_top", + "topBtn": "nUhMVa_topBtn", + "version": "nUhMVa_version", + "viewBar": "nUhMVa_viewBar", + "viewBtn": "nUhMVa_viewBtn", + "viewOn": "nUhMVa_viewOn", + "warnLine": "nUhMVa_warnLine" + }; + //#endregion + //#region src/client/comments.ts + /** + * giscus wiring for the in-market comment thread. + * + * A plugin has ONE discussion, whether the reader reached it from + * dshmarket.com, from the awesome-dsh-plugin catalog, or from inside the + * market itself. That is only true as long as three things agree across the + * three surfaces: the repository ids below, the `plugin:` term, and the + * slug those terms are built from. The ids here mirror site/comments.mjs and + * the slug mirrors scripts/build-site.mjs; both are asserted in + * tests/client/comments.client.spec.ts against the real files, because a + * silent disagreement does not fail — it forks the conversation in half and + * nobody notices until someone asks why their comment vanished. + */ + const GISCUS = Object.freeze({ + repo: "awesome-dsh-plugin/awesome-dsh-plugin", + repoId: "R_kgDOT3ajCQ", + category: "Plugins", + categoryId: "DIC_kwDOT3ajCc4DES8_" + }); + /** + * The plugin's slug, derived from its GitHub URL exactly as the two site + * builders derive it. Entries that live in a subdirectory of a monorepo + * (`.../tree//packages/x`) get the `owner/repo--packages-x` form, so two + * plugins from one repository do not share a thread. + */ + function pluginSlug(url) { + const repoPath = url.replace("https://github.com/", ""); + const repo = repoPath.split("/").slice(0, 2).join("/"); + const sub = repoPath.includes("/tree/") ? repoPath.split("/tree/")[1].replace(/^[^/]+\//, "") : null; + return sub ? `${repo}--${sub.replaceAll("/", "-")}` : repo; + } + /** The giscus discussion term for a plugin. */ + const commentsTerm = (url) => `plugin:${pluginSlug(url)}`; + /** giscus's own locale codes, which are not the ones this plugin uses. */ + const giscusLang = (lang) => lang === "zh" ? "zh-CN" : "en"; + //#endregion + //#region src/client/CommentsModal.tsx + /** + * The comment thread for one plugin, rendered inside the market. + * + * Opening this is the request to read the comments, so it loads on open — + * there is no second click to confirm what the first one already said. What + * the reader does have to be told, once and plainly, is that reading here + * contacts giscus.app and GitHub: nothing else in the market talks to a third + * party at the reader's request, and a comment box that quietly opens that + * connection would be a surprise, not a feature. + * + * The thread itself lives in GitHub Discussions, keyed on the same + * `plugin:` term the two websites use — see comments.ts. + */ + /** + * Whether the host is currently showing a dark theme. + * + * Read off the rendered page rather than from `prefers-color-scheme`: the host + * lets the reader pick a theme (including custom ones) independently of the + * OS, and a light comment box dropped into a dark window is the kind of seam + * that makes an embed look bolted on. + */ + function hostIsDark() { + if (typeof window === "undefined") return false; + const bg = getComputedStyle(document.body).backgroundColor; + const m = /rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/.exec(bg); + if (!m) return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false; + const [r, g, b] = [ + Number(m[1]), + Number(m[2]), + Number(m[3]) + ]; + return .299 * r + .587 * g + .114 * b < 128; + } + function CommentsModal(props) { + const { name, url, lang, onClose, t } = props; + const mount = (0, react.useRef)(null); + const [state, setState] = (0, react.useState)("loading"); + const [attempt, setAttempt] = (0, react.useState)(0); + (0, react.useEffect)(() => { + const host = mount.current; + if (host === null) return; + host.replaceChildren(); + setState("loading"); + const script = document.createElement("script"); + script.src = "https://giscus.app/client.js"; + script.async = true; + script.crossOrigin = "anonymous"; + Object.assign(script.dataset, { + repo: GISCUS.repo, + repoId: GISCUS.repoId, + category: GISCUS.category, + categoryId: GISCUS.categoryId, + mapping: "specific", + term: commentsTerm(url), + strict: "1", + reactionsEnabled: "1", + emitMetadata: "0", + inputPosition: "bottom", + theme: hostIsDark() ? "dark" : "light", + lang: giscusLang(lang), + loading: "lazy" + }); + script.onload = () => setState("ready"); + script.onerror = () => { + script.remove(); + setState("failed"); + }; + host.append(script); + return () => { + host.replaceChildren(); + }; + }, [ + url, + lang, + attempt + ]); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, { + open: true, + onClose, + title: t("commentsTitle") + " — " + name, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.commentsNote, + children: t("commentsNote") + }), + state === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.commentsFail, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.commentsError, + children: t("commentsError") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: () => setAttempt((n) => n + 1), + children: t("commentsRetry") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.src, + href: `https://github.com/${GISCUS.repo}/discussions`, + target: "_blank", + rel: "noreferrer", + children: t("commentsOnGitHub") + }) + ] + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.commentsStatus, + role: "status", + "aria-live": "polite", + children: state === "loading" ? t("commentsLoading") : "" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + ref: mount, + className: Market_module_css_default.commentsMount, + "aria-busy": state === "loading" + }) + ] + }); + } + //#endregion + //#region src/client/operations.ts + const BUCKETS = { + queued: "busy", + running: "busy", + input: "attention", + failed: "attention", + done: "ok", + warned: "ok" + }; + /** Which of the three visual groups a state belongs to. */ + function bucketOf(state) { + return BUCKETS[state]; + } + /** Whether a record has stopped moving on its own. */ + function isSettled(record) { + return record.state === "done" || record.state === "warned" || record.state === "failed"; + } + /** Whether a record is waiting on the user rather than on the host. */ + function needsUser(record) { + return record.state === "input"; + } + /** Append a record. Ids are supplied by the caller so tests stay deterministic. */ + function enqueue(list, record) { + return [...list, record]; + } + /** + * Apply changes to one record. + * @returns a new list; unchanged (same contents) when no record matches. + */ + function patch(list, id, changes) { + return list.map((record) => record.id === id ? { + ...record, + ...changes + } : record); + } + /** Drop one record outright — the panel's per-row dismiss. */ + function drop(list, id) { + return list.filter((record) => record.id !== id); + } + /** + * Clear finished records, keeping anything still moving or still waiting on + * the user. A record in `input` survives: the user has not answered it yet, + * and clearing it would delete the only route back to that decision. + */ + function clearSettled(list) { + return list.filter((record) => !isSettled(record)); + } + /** + * How many queued records sit ahead of this one. The panel shows it because + * "queued" without a position reads as stuck; the host runs one mutation at a + * time, so the order shown is the order that will run. + * @returns the count ahead, or null when the record is not queued. + */ + function queuePosition(list, id) { + const index = list.filter((record) => record.state === "queued").findIndex((record) => record.id === id); + return index < 0 ? null : index; + } + /** + * Counts for the panel entry. The entry reports the batch, never one row: + * one aggregate line beats seven notifications racing each other. + */ + function summarize(list) { + let running = 0; + let queued = 0; + let attention = 0; + let settled = 0; + for (const record of list) if (record.state === "running") running += 1; + else if (record.state === "queued") queued += 1; + else if (needsUser(record)) attention += 1; + else settled += 1; + return { + running, + queued, + attention, + settled, + total: running + queued + settled, + progressed: settled + running + }; + } + /** + * Panel order: what needs the user first, then what is still moving, then + * what is finished. Within a group the original order is kept so a queue + * reads top-to-bottom in the order it will run. + */ + function sortForPanel(list) { + const rank = (record) => needsUser(record) ? 0 : isSettled(record) ? 2 : 1; + return [...list].map((record, index) => ({ + record, + index + })).sort((a, b) => rank(a.record) - rank(b.record) || a.index - b.index).map((entry) => entry.record); + } + /** + * The record a plugin card should reflect, newest first. A card shows the + * button state for an operation in flight and a terminal marker for one that + * ended without installing — without that marker a rejected install leaves + * the card looking untouched, and the obvious next move is to press Install + * again. + */ + function recordForUrl(list, url) { + for (let index = list.length - 1; index >= 0; index -= 1) { + const record = list[index]; + if (record.url === url) return record; + } + return null; + } + //#endregion + //#region src/client/OperationsPanel.tsx + /** + * The activity entry and its panel: every install, update and uninstall the + * user started, with the action each outcome calls for. + * + * The entry lives in the tab row rather than above the plugin grid, so + * paginating, searching or switching tab cannot take a record — or a pending + * decision — off screen. It reports the batch as one aggregate ("installing + * 3 / 7") instead of one line per plugin. + */ + /** What the two clash outcomes are, in the order they are offered. */ + const CHOICES = [{ + id: "keep", + label: "conflictKeep", + note: "conflictKeepNote" + }, { + id: "swap", + label: "conflictSwap", + note: "conflictSwapNote" + }]; + /** + * What each plugin ends up as under the selected outcome. + * + * The consequence is drawn ON the plugins rather than described beside them: + * pick "keep what I have" and the installed rows tick while the candidate + * crosses out; pick the other and they swap. The candidate is in this list + * for the same reason — a decision about which plugin survives has to show + * what happens to the one being installed, not only to the others. + */ + function OutcomePreview(props) { + const { t, record, choice } = props; + const swap = choice === "swap"; + const candidate = props.describe(record.name); + const row = (key, info, kept, tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: kept ? Market_module_css_default.rosterRow : `${Market_module_css_default.rosterRow} ${Market_module_css_default.rosterRowOut}`, + children: [ + info.avatar, + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.rosterMain, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.rosterName, + title: key, + children: info.title + }), info.author !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.rosterAuthor, + children: info.author + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: kept ? `${Market_module_css_default.rosterTag} ${Market_module_css_default.rosterTagKeep}` : `${Market_module_css_default.rosterTag} ${Market_module_css_default.rosterTagDrop}`, + children: [ + kept ? "✓" : "✕", + " ", + tag + ] + }) + ] + }, key); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.roster, + children: [ + row(record.name, candidate, swap, t(swap ? "conflictOutcomeInstall" : "conflictOutcomeSkip")), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: Market_module_css_default.rosterSplit }), + (record.conflicts ?? []).map((group) => row(group.owner, props.describe(group.owner), !swap, t(swap ? "conflictOutcomeRemove" : "conflictOutcomeKeep"))) + ] + }); + } + /** + * The decision attached to a clash. Two outcomes rather than an error plus a + * destructive button: the default changes nothing, and selecting the other + * one is itself the consent step, so its cost is stated here. + */ + function ConflictChoice(props) { + const { t, record, replacing, envReady } = props; + const [choice, setChoice] = (0, react.useState)("keep"); + const [whyOpen, setWhyOpen] = (0, react.useState)(false); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.opDecision, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.conflictBody, + children: t("conflictBody") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutcomePreview, { + t, + record, + choice, + describe: props.describe + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.choices, + children: CHOICES.map(({ id, label, note }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { + className: choice === id ? `${Market_module_css_default.choice} ${Market_module_css_default.choiceOn}` : Market_module_css_default.choice, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", { + type: "radio", + className: Market_module_css_default.choiceRadio, + name: `dshm-conflict-${record.id}`, + checked: choice === id, + disabled: replacing, + onChange: () => setChoice(id) + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.choiceMain, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.choiceTitle, + children: t(label) + }), t(note) !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.choiceNote, + children: t(note) + })] + })] + }, id)) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.opDecisionFoot, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { + type: "button", + className: Market_module_css_default.conflictDetailsToggle, + "aria-expanded": whyOpen, + onClick: () => setWhyOpen((open) => !open), + children: [t("conflictDetails"), whyOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 12 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 12 })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: choice === "swap" ? "outline" : "primary", + size: "sm", + className: choice === "swap" ? Market_module_css_default.dangerBtn : void 0, + disabled: replacing || choice === "swap" && !envReady, + onClick: () => props.onResolve(choice), + children: replacing ? t("conflictReplacing") : t("confirm") + }) + ] + }), + whyOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.conflictWhy, + children: [(record.conflicts ?? []).map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [ + group.owner, + ": ", + group.ids.join(", ") + ] }, group.owner)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.conflictWhyText, + children: t("conflictWhy") + })] + }) + ] + }); + } + /** Icon for a record's visual bucket — three, not one per state. */ + function BucketIcon(props) { + const bucket = bucketOf(props.record.state); + if (bucket === "busy") return props.record.state === "running" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spin, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutline16, { size: 13 }) + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.opQueuedIcon, + children: "⋯" + }); + if (bucket === "ok") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { + size: 13, + className: Market_module_css_default.reassureOk + }); + return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { + size: 14, + className: Market_module_css_default.conflictIcon + }); + } + /** The one-line status under a record's name; the bucket carries the rest. */ + function statusLine(t, record, ahead) { + switch (record.state) { + case "queued": return ahead === null || ahead === 0 ? t("opQueued") : `${t("opQueued")} · ${t("opQueuedAhead")} ${String(ahead)}`; + case "running": return record.detail ?? t("opRunning"); + case "input": return t("opNeedsChoice"); + case "failed": return record.reason ?? t("installFail"); + case "warned": return record.reason ?? t("opDone"); + case "done": return record.needsRefresh === true ? t("opDoneRefresh") : t("opDone"); + } + } + function OperationsPanel(props) { + const { t, records, open } = props; + const setOpen = props.onOpenChange; + const wrapRef = (0, react.useRef)(null); + const summary = summarize(records); + const busy = summary.running + summary.queued > 0; + (0, react.useEffect)(() => { + if (!open) return void 0; + const onKey = (event) => { + if (event.key === "Escape") setOpen(false); + }; + const onPointer = (event) => { + const wrap = wrapRef.current; + if (wrap !== null && !wrap.contains(event.target)) setOpen(false); + }; + document.addEventListener("keydown", onKey); + document.addEventListener("mousedown", onPointer); + return () => { + document.removeEventListener("keydown", onKey); + document.removeEventListener("mousedown", onPointer); + }; + }, [open, setOpen]); + const label = busy ? `${t("opInstalling")} ${String(summary.progressed)}/${String(summary.total)}` : summary.attention > 0 ? `${String(summary.attention)} ${t("opNeedsYou")}` : t("opTitle"); + const quiet = records.length === 0 && !open; + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.opWrap, + ref: wrapRef, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { + type: "button", + className: quiet ? `${Market_module_css_default.opEntry} ${Market_module_css_default.opEntryQuiet}` : summary.attention > 0 ? `${Market_module_css_default.opEntry} ${Market_module_css_default.opEntryAlert}` : Market_module_css_default.opEntry, + "aria-expanded": open, + onClick: () => setOpen(quiet ? true : !open), + children: [ + !quiet && busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spin, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutline16, { size: 12 }) + }), + quiet ? t("opTitle") : label, + !quiet && summary.attention > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.opDot }) + ] + }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.opPanel, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.opHead, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.opPanelTitle, + children: t("opTitle") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + summary.settled > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: props.onClearSettled, + children: t("opClear") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + "aria-label": t("opClose"), + title: t("opClose"), + className: Market_module_css_default.opCloseBtn, + onClick: () => setOpen(false), + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 }) + }) + ] + }), + busy && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.opAggregate, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.opAggregateTop, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [ + t("opInstalling"), + " ", + summary.progressed, + "/", + summary.total + ] }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.bar, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.barFill, + style: { width: `${String(Math.round(summary.progressed / Math.max(1, summary.total) * 100))}%` } + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.opAggregateHint, + children: t("opLeaveHint") + }) + ] + }), + records.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.opEmpty, + children: [t("opEmpty"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.opEmptyHint, + children: t("opEmptyHint") + })] + }), + sortForPanel(records).map((record) => { + const ahead = queuePosition(records, record.id); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: needsUser(record) ? `${Market_module_css_default.opRow} ${Market_module_css_default.opRowAlert}` : Market_module_css_default.opRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.opIcon, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BucketIcon, { record }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.opMain, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.opTop, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.opVerb, + children: t(`opKind_${record.kind}`) + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.opName, + title: record.name, + children: record.name + })] + }), + record.state === "running" && typeof record.percent === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.bar, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.barFill, + style: { width: `${String(record.percent)}%` } + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: bucketOf(record.state) === "attention" ? `${Market_module_css_default.opStatus} ${Market_module_css_default.opStatusBad}` : Market_module_css_default.opStatus, + children: statusLine(t, record, ahead) + }), + needsUser(record) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConflictChoice, { + t, + record, + replacing: props.replacing, + envReady: props.envReady, + describe: props.describe, + onResolve: (choice) => props.onResolveConflict(record, choice) + }) + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.opActions, + children: [ + record.state === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: () => props.onCancel(record), + children: t("cancelOp") + }), + record.state === "queued" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => props.onDismiss(record), + children: t("opDequeue") + }), + record.state === "done" && record.needsRefresh === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + onClick: props.onRefresh, + children: t("refresh") + }), + record.state === "failed" && (record.blockedBuilds ?? []).length > 0 && props.onApproveBuilds !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + onClick: () => props.onApproveBuilds?.(record), + children: t("approveBuilds") + }), + record.state === "failed" && (record.blockedBuilds ?? []).length === 0 && props.onRetry !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: () => props.onRetry?.(record), + children: t("opRetry") + }), + isSettled(record) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => props.onDismiss(record), + children: t("dismissNotice") + }) + ] + }) + ] + }, record.id); + }) + ] + })] + }); + } + //#endregion + //#region src/client/preset-panel.tsx + /** + * Diagnostics sub-panel — issue #98 phase 3 (plugin presets). + * + * Rendered at the bottom of the Diagnostics tab inside a collapsible section + * (the parent keeps it mounted and passes `open`, so it lazy-loads on first + * expand and keeps its state across collapses). Lists named presets + * (GET /dsh-market/presets), saves the current community bundle order as a + * new preset (POST /dsh-market/presets {action:'save', name, bundleOrder, + * disabled}), applies one (action:'apply' — the host trial-validates and + * auto-snapshots before writing; on success the parent refreshes the check + * report) and deletes one after an inline double confirmation + * (action:'delete'). + * + * The list payload is read defensively (bare array or {presets: [...]}). + */ + async function postJson$1(url, body) { + let res; + try { + res = await fetch(url, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(body) + }); + } catch { + return { + status: 0, + body: null + }; + } + let payload = null; + try { + payload = await res.json(); + } catch {} + return { + status: res.status, + body: payload + }; + } + const errorText = (status, body, fallback) => body !== null && typeof body.error === "string" ? body.error : status === 0 ? fallback + "network error" : fallback + `HTTP ${String(status)}`; + function PresetPanel(props) { + const { t, open, bundleOrder, onRefresh } = props; + const [presets, setPresets] = (0, react.useState)(null); + const [error, setError] = (0, react.useState)(null); + const [msg, setMsg] = (0, react.useState)(null); + const [busy, setBusy] = (0, react.useState)(null); + const [name, setName] = (0, react.useState)(""); + /** Preset name awaiting the second confirm click, or null. */ + const [confirmDelete, setConfirmDelete] = (0, react.useState)(null); + /** Hidden for the import action. */ + const loaded = (0, react.useRef)(false); + const load = (0, react.useCallback)(() => { + fetch(api("/dsh-market/presets"), { cache: "no-store" }).then((res) => res.json()).then((body) => { + const list = Array.isArray(body) ? body : Array.isArray(body.presets) ? body.presets : []; + setPresets(list.map((item) => { + const preset = item ?? {}; + return { + name: String(preset.name ?? ""), + bundleOrder: Array.isArray(preset.bundleOrder) ? preset.bundleOrder.map(String) : [], + disabled: Array.isArray(preset.disabled) ? preset.disabled.map(String) : [] + }; + })); + setError(null); + loaded.current = true; + }).catch(() => setError(t("presetListFail") + "network")); + }, [t]); + (0, react.useEffect)(() => { + if (open && !loaded.current) load(); + }, [open, load]); + (0, react.useCallback)(() => { + const presetName = name.trim(); + if (presetName === "") { + setError(t("presetNameEmpty")); + return; + } + if (busy !== null) return; + setBusy("save"); + setMsg(null); + setError(null); + fetch(api("/dsh-market/installed"), { cache: "no-store" }).then((res) => res.json()).then((installed) => postJson$1(api("/dsh-market/presets"), { + action: "save", + name: presetName, + bundleOrder, + disabled: Array.isArray(installed.disabled) ? installed.disabled.map(String) : [] + })).then(({ status, body }) => { + if (status >= 200 && status < 300 && body?.ok === true) { + setName(""); + setMsg(t("presetSaved")); + load(); + } else setError(errorText(status, body, t("presetFail"))); + }).catch(() => setError(t("presetFail") + "network")).finally(() => setBusy(null)); + }, [ + busy, + bundleOrder, + load, + name, + t + ]); + const apply = (0, react.useCallback)((presetName) => { + if (busy !== null) return; + setBusy("apply"); + setMsg(null); + setError(null); + postJson$1(api("/dsh-market/presets"), { + action: "apply", + name: presetName + }).then(({ status, body }) => { + if (status >= 200 && status < 300 && body?.ok === true) { + setMsg(t("presetApplied")); + onRefresh(); + } else setError(errorText(status, body, t("presetFail"))); + }).catch(() => setError(t("presetFail") + "network")).finally(() => setBusy(null)); + }, [ + busy, + onRefresh, + t + ]); + const remove = (0, react.useCallback)((presetName) => { + if (busy !== null) return; + setBusy("delete"); + setMsg(null); + setError(null); + postJson$1(api("/dsh-market/presets"), { + action: "delete", + name: presetName + }).then(({ status, body }) => { + if (status >= 200 && status < 300 && body?.ok === true) { + setConfirmDelete(null); + setMsg(t("presetDeleted")); + load(); + } else setError(errorText(status, body, t("presetFail"))); + }).catch(() => setError(t("presetFail") + "network")).finally(() => setBusy(null)); + }, [ + busy, + load, + t + ]); + /** Change preview of one preset: {reordered[], enabled[], disabled[], noop}. */ + const [previewed, setPreviewed] = (0, react.useState)(null); + const preview = (0, react.useCallback)((presetName) => { + if (busy !== null) return; + setBusy("preview"); + setMsg(null); + setError(null); + if (previewed?.name === presetName) { + setPreviewed(null); + setBusy(null); + return; + } + postJson$1(api("/dsh-market/presets"), { + action: "preview", + name: presetName + }).then(({ status, body }) => { + if (status >= 200 && status < 300 && body?.ok === true && body.changes !== null && typeof body.changes === "object") { + const changes = body.changes; + setPreviewed({ + name: presetName, + changes: { + reordered: Array.isArray(changes.reordered) ? changes.reordered.map(String) : [], + enabled: Array.isArray(changes.enabled) ? changes.enabled.map(String) : [], + disabled: Array.isArray(changes.disabled) ? changes.disabled.map(String) : [], + noop: changes.noop === true + } + }); + } else setError(errorText(status, body, t("presetPreviewFail"))); + }).catch(() => setError(t("presetPreviewFail") + "network")).finally(() => setBusy(null)); + }, [ + busy, + previewed, + t + ]); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.orderPanel, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.panelNote, + children: t("presetHint") + }), + error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.err, + children: error + }), + presets === null || presets.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagEmpty, + children: t("presetEmpty") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.presetList, + children: presets.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.presetRow, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.confirmRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.presetName, + children: preset.name + }), + preset.bundleOrder.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + children: t("presetBundleCount").replace("{0}", String(preset.bundleOrder.length)) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + confirmDelete === preset.name ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.confirmRow, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: busy !== null, + onClick: () => remove(preset.name), + children: t("presetDelete") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + disabled: busy !== null, + onClick: () => setConfirmDelete(null), + children: t("cancel") + })] + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.confirmRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: busy !== null, + onClick: () => apply(preset.name), + children: t("presetApply") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + disabled: busy !== null, + onClick: () => preview(preset.name), + children: previewed?.name === preset.name ? t("cancel") : t("presetPreview") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + disabled: busy !== null, + onClick: () => setConfirmDelete(preset.name), + children: t("presetDelete") + }) + ] + }) + ] + }), previewed?.name === preset.name && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagList, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagKey, + children: t("presetPreviewTitle") + }), previewed.changes.noop ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("presetNoop") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.spec, + children: [ + previewed.changes.reordered.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.warnLine, + children: [ + t("presetReorder").replace("{0}", String(previewed.changes.reordered.length)), + ": ", + previewed.changes.reordered.join(", ") + ] + }), + previewed.changes.enabled.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.warnLine, + children: [ + t("presetEnable").replace("{0}", String(previewed.changes.enabled.length)), + ": ", + previewed.changes.enabled.join(", ") + ] + }), + previewed.changes.disabled.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.warnLine, + children: [ + t("presetDisable").replace("{0}", String(previewed.changes.disabled.length)), + ": ", + previewed.changes.disabled.join(", ") + ] + }) + ] + })] + })] + }, preset.name)) + }), + msg !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.okState, + children: msg + }) + ] + }); + } + //#endregion + //#region src/client/snapshot-panel.tsx + /** + * Diagnostics sub-panel — issue #98 phase 3 (snapshots & rollback). + * + * Rendered at the bottom of the Diagnostics tab inside a collapsible section + * (the parent keeps it mounted and passes `open`, so it lazy-loads on first + * expand and keeps its state across collapses). Lists profile snapshots + * (GET /dsh-market/snapshots), creates one (POST /dsh-market/snapshots) and + * restores one after an inline double confirmation + * (POST /dsh-market/restore-snapshot {snapshot}). + * + * The list payload is read defensively (bare array or {snapshots: [...]}); + * `files` entries are {path, json|lines} objects, from which only `path` is + * displayed. + */ + async function postJson(url, body) { + let res; + try { + res = await fetch(url, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(body) + }); + } catch { + return { + status: 0, + body: null + }; + } + let payload = null; + try { + payload = await res.json(); + } catch {} + return { + status: res.status, + body: payload + }; + } + /** Snapshot file entries may be plain names or {path, json|lines} objects. */ + function fileNames(files) { + if (!Array.isArray(files)) return []; + const names = []; + for (const file of files) if (typeof file === "string") names.push(file); + else if (file !== null && typeof file === "object") { + const path = file.path; + if (typeof path === "string") names.push(path); + } + return names; + } + function snapshotOf(value) { + if (value === null || typeof value !== "object") return null; + const item = value; + if (typeof item.id !== "string" && typeof item.id !== "number") return null; + return { + id: String(item.id), + createdAt: typeof item.createdAt === "string" || typeof item.createdAt === "number" ? item.createdAt : 0, + files: fileNames(item.files) + }; + } + function formatTime(value) { + const date = new Date(value); + return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString(); + } + function SnapshotPanel(props) { + const { t, open, onRefresh } = props; + const [snapshots, setSnapshots] = (0, react.useState)(null); + const [error, setError] = (0, react.useState)(null); + const [msg, setMsg] = (0, react.useState)(null); + const [busy, setBusy] = (0, react.useState)(null); + /** Snapshot id awaiting the second confirm click (restore), or null. */ + const [confirmId, setConfirmId] = (0, react.useState)(null); + /** Snapshot id awaiting the second confirm click (delete), or null. */ + const [confirmDeleteId, setConfirmDeleteId] = (0, react.useState)(null); + const loaded = (0, react.useRef)(false); + const load = (0, react.useCallback)(() => { + fetch(api("/dsh-market/snapshots"), { cache: "no-store" }).then((res) => res.json()).then((body) => { + const list = Array.isArray(body) ? body : Array.isArray(body.snapshots) ? body.snapshots : []; + setSnapshots(list.map(snapshotOf).filter((snap) => snap !== null)); + setError(null); + loaded.current = true; + }).catch(() => setError(t("snapListFail") + "network")); + }, [t]); + (0, react.useEffect)(() => { + if (open && !loaded.current) load(); + }, [open, load]); + const create = (0, react.useCallback)(() => { + if (busy !== null) return; + setBusy("create"); + setMsg(null); + setError(null); + postJson(api("/dsh-market/snapshots"), {}).then(({ status, body }) => { + if (status >= 200 && status < 300 && body?.ok === true) { + setMsg(t("snapCreated")); + load(); + } else { + const detail = body !== null && typeof body.error === "string" ? body.error : status === 0 ? "network error" : `HTTP ${String(status)}`; + setError(t("snapCreateFail") + detail); + } + }).catch(() => setError(t("snapCreateFail") + "network")).finally(() => setBusy(null)); + }, [ + busy, + load, + t + ]); + const restore = (0, react.useCallback)((id) => { + if (busy !== null) return; + setBusy("restore"); + setMsg(null); + setError(null); + postJson(api("/dsh-market/restore-snapshot"), { snapshot: id }).then(({ status, body }) => { + if (status >= 200 && status < 300 && body?.ok === true) { + setConfirmId(null); + setMsg(t("snapRestored")); + onRefresh(); + } else { + const detail = body !== null && typeof body.error === "string" ? body.error : status === 0 ? "network error" : `HTTP ${String(status)}`; + setError(t("snapRestoreFail") + detail); + } + }).catch(() => setError(t("snapRestoreFail") + "network")).finally(() => setBusy(null)); + }, [ + busy, + onRefresh, + t + ]); + /** Delete one snapshot after inline double confirmation. */ + const remove = (0, react.useCallback)((id) => { + if (busy !== null) return; + setBusy("delete"); + setMsg(null); + setError(null); + postJson(api("/dsh-market/delete-snapshot"), { snapshot: id }).then(({ status, body }) => { + if (status >= 200 && status < 300 && body?.ok === true) { + setConfirmDeleteId(null); + setMsg(t("snapDeleted")); + load(); + } else { + const detail = body !== null && typeof body.error === "string" ? body.error : status === 0 ? "network error" : `HTTP ${String(status)}`; + setError(t("snapDeleteFail") + detail); + } + }).catch(() => setError(t("snapDeleteFail") + "network")).finally(() => setBusy(null)); + }, [ + busy, + load, + t + ]); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.orderPanel, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.panelNote, + children: t("snapHint") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.panelActions, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: busy !== null, + onClick: create, + children: busy === "create" ? t("snapCreating") : t("snapCreate") + }) + }), + error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.err, + children: error + }), + snapshots === null || snapshots.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagEmpty, + children: t("snapEmpty") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.snapList, + children: snapshots.map((snap) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.snapRow, + children: confirmId === snap.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.snapConfirmText, + children: t("snapRestoreConfirmText") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.confirmRow, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: busy !== null, + onClick: () => restore(snap.id), + children: busy === "restore" ? t("snapRestoring") : t("snapRestoreConfirm") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + disabled: busy !== null, + onClick: () => setConfirmId(null), + children: t("cancel") + })] + })] }) : confirmDeleteId === snap.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.snapConfirmText, + children: t("snapDeleteConfirmText") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.confirmRow, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: busy !== null, + onClick: () => remove(snap.id), + children: busy === "delete" ? t("snapDeleting") : t("snapDeleteConfirm") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + disabled: busy !== null, + onClick: () => setConfirmDeleteId(null), + children: t("cancel") + })] + })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.snapMeta, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagVal, + children: formatTime(snap.createdAt) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + children: snap.id + }), + snap.files.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + t("snapFiles"), + ": ", + snap.files.join(", ") + ] + }) + ] + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.confirmRow, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: busy !== null, + onClick: () => setConfirmId(snap.id), + children: t("snapRestore") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + disabled: busy !== null, + onClick: () => setConfirmDeleteId(snap.id), + children: t("snapDelete") + })] + })] }) + }, snap.id)) + }), + msg !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.okState, + children: msg + }) + ] + }); + } + //#endregion + //#region src/client/Diagnostics.tsx + /** + * Diagnostics tab — issue #98: renders the profile composition check report + * served by the host route /dsh-market/check (see src/check.ts). Below the + * report sit the phase 2/3 action panels: a community-bundle ordering block + * (reorder locally with ↑/↓, POST to /dsh-market/bundle-order) and a + * snapshots & rollback panel (snapshot-panel.tsx) — collapsible, default + * collapsed, and lazy-fetching on first expand. The plugin presets panel + * ships in a later stacked PR. + * + * Read-only view of the loading-layer stack and the conflict surface: bundle + * order (official vs community), duplicate loader entry ids, peer dependency + * mismatches, multi-version core packages, overrides and orphan patches. The + * report + * shape mirrors the CheckReport interface in src/check.ts; it is re-declared + * here because the client bundle is built independently of the host tree. + */ + /** + * A collapsible report section: header shows title + count + chevron; the + * body stays mounted (hidden via CSS when collapsed) so every block keeps + * its state. ALL blocks are collapsed by default — the summary strip above + * gives the overview, and a problem block's title is highlighted and its + * collapsed `overview` line shows the first issue, so nothing important is + * hidden. Expand a block to see its full content. + */ + function Section(props) { + const { title, count, empty, defaultOpen, problem = true, overview, alwaysShowBody = false, children } = props; + const [open, setOpen] = (0, react.useState)(defaultOpen ?? false); + const alert = problem && count > 0; + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", { + className: Market_module_css_default.diagSection, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { + type: "button", + className: Market_module_css_default.collapseHead, + onClick: () => setOpen((o) => !o), + "aria-expanded": open, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.collapseIcon, + children: open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { size: 14 }) + }), + alert && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagAlert, + children: "⚠" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: `${Market_module_css_default.collapseTitle}${alert ? ` ${Market_module_css_default.diagAlert}` : ""}`, + children: title + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.diagCount, + children: [ + "(", + count, + ")" + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }) + ] + }), + !open && overview !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.sectionOverview, + children: overview + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.collapseBody, + style: open ? void 0 : { display: "none" }, + children: count === 0 && !alwaysShowBody ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagEmpty, + children: empty + }) : children + }) + ] + }); + } + /** A collapsible section that KEEPS its children mounted (hidden via CSS when + * collapsed) so the phase 3 panels below retain their loaded data and + * in-progress edits across collapses. Children mount from the start, but the + * panels only fetch when `open` first becomes true. + */ + function CollapsibleSection(props) { + const { title, count, open, onToggle, children } = props; + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", { + className: Market_module_css_default.diagSection, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { + type: "button", + className: Market_module_css_default.collapseHead, + onClick: onToggle, + "aria-expanded": open, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.collapseIcon, + children: open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { size: 14 }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.collapseTitle, + children: title + }), + count !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.diagCount, + children: [ + "(", + count, + ")" + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }) + ] + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.collapseBody, + style: open ? void 0 : { display: "none" }, + children + })] + }); + } + /** Map an orphan patch reason (src/check.ts) to a locale key for its badge. */ + function orphanKindLabel(reason) { + if (reason === "insert is not an array") return "orphanInsertNotArray"; + if (reason === "insert target not found") return "orphanInsertTargetMissing"; + if (reason === "insert target is not a group") return "orphanInsertTargetNotGroup"; + if (reason === "id required for non-insert patch") return "orphanIdRequired"; + if (reason === "patch target not found") return "orphanPatchTargetMissing"; + if (reason.startsWith("name mismatch")) return "orphanNameMismatch"; + return "orphanReasonOther"; + } + /** Badge label for one risk-tier peer row (#201). */ + function peerRiskLabel(peer, t) { + return peer.verdict?.kind === "risk" && peer.verdict.risk.direction === "aboveMax" ? t("peerRiskAboveMax") : t("peerRiskBelowMin"); + } + /** Badge label for one warning-tier peer row (optional / aboveMax / legacy). */ + function peerWarningLabel(peer, t) { + if (peer.verdict?.kind !== "warning") return t("checkUnsatisfied"); + return peer.verdict.warning.reason === "optional" ? t("peerWarnOptional") : t("peerWarnAboveMax"); + } + /** + * Fetch and render the profile check report. Refetches on every mount, so + * switching tabs away and back re-runs the (cheap, read-only) analysis; the + * phase 3 panels below call `refresh()` after applying changes. + */ + function Diagnostics(props) { + const { t } = props; + const [report, setReport] = (0, react.useState)(null); + const [error, setError] = (0, react.useState)(null); + const [orderOpen, setOrderOpen] = (0, react.useState)(false); + const [explainOpen, setExplainOpen] = (0, react.useState)(false); + const [snapOpen, setSnapOpen] = (0, react.useState)(false); + const [presetOpen, setPresetOpen] = (0, react.useState)(false); + const [fixMsg, setFixMsg] = (0, react.useState)(null); + /** The built AI-fix prompt when the clipboard path failed — rendered as a + * selectable text block so the user can still copy it manually. */ + const [fixFallback, setFixFallback] = (0, react.useState)(null); + /** Bump to re-run the /dsh-market/check fetch after an order/snapshot-restore apply. */ + const [version, setVersion] = (0, react.useState)(0); + const refresh = (0, react.useCallback)(() => setVersion((v) => v + 1), []); + /** Community bundle names from the report, in declared order. */ + const communityNames = (0, react.useMemo)(() => report === null ? [] : report.bundles.filter((bundle) => bundle.kind === "community").map((bundle) => bundle.name), [report]); + /** Local editing state: re-synced whenever the report (re)loads. */ + const [order, setOrder] = (0, react.useState)(communityNames); + const [orderMsg, setOrderMsg] = (0, react.useState)(null); + const [orderErr, setOrderErr] = (0, react.useState)(null); + const [orderBusy, setOrderBusy] = (0, react.useState)(false); + /** Current-vs-candidate composition diff from a rejected static-composition + * validation (#125 review): what the candidate would change, shown as a hint. */ + const [orderDiff, setOrderDiff] = (0, react.useState)(null); + /** + * Content identity of the last community order this draft synced to. A + * refresh() refetch returns a NEW array even when the order is unchanged, + * so a naive `setOrder(communityNames)` effect would wipe the user's + * in-progress drag/↑↓ edits on every unrelated re-check. Only resync when + * the report's community order actually CHANGED (apply order / snapshot + * restore) — an identical refetch keeps the draft (review M2). + */ + const syncedOrderRef = (0, react.useRef)(null); + (0, react.useEffect)(() => { + const synced = syncedOrderRef.current; + if (synced !== null && synced.length === communityNames.length && communityNames.every((name, i) => name === synced[i])) return; + syncedOrderRef.current = communityNames; + setOrder(communityNames); + }, [communityNames]); + /** Swap one community bundle with its neighbour (-1 up, +1 down). */ + const moveBundle = (index, delta) => { + setOrder((prev) => { + const next = [...prev]; + const target = index + delta; + if (target < 0 || target >= next.length) return prev; + [next[index], next[target]] = [next[target], next[index]]; + return next; + }); + }; + /** Row being dragged (index into the local `order` draft). */ + const [dragIndex, setDragIndex] = (0, react.useState)(null); + /** Row currently under the pointer, highlighted as the drop target. */ + const [dragOverIndex, setDragOverIndex] = (0, react.useState)(null); + const onRowDragStart = (index) => (event) => { + if (orderBusy) { + event.preventDefault(); + return; + } + setDragIndex(index); + event.dataTransfer?.setData?.("text/plain", order[index] ?? ""); + if (event.dataTransfer !== void 0) event.dataTransfer.effectAllowed = "move"; + }; + const onRowDragOver = (index) => (event) => { + if (dragIndex === null || dragIndex === index) return; + event.preventDefault(); + if (event.dataTransfer !== void 0) event.dataTransfer.dropEffect = "move"; + setDragOverIndex(index); + }; + const onRowDragLeave = (index) => () => { + setDragOverIndex((prev) => prev === index ? null : prev); + }; + const onRowDrop = (index) => (event) => { + event.preventDefault(); + const from = dragIndex; + setDragIndex(null); + setDragOverIndex(null); + if (from === null || from === index) return; + setOrder((prev) => { + const next = [...prev]; + const [moved] = next.splice(from, 1); + next.splice(index, 0, moved); + return next; + }); + }; + const onRowDragEnd = () => { + setDragIndex(null); + setDragOverIndex(null); + }; + /** POST the current community order; the host statically validates the + * candidate composition (dry-run replay) before writing. */ + const applyOrder = (target) => { + if (orderBusy) return; + setOrderBusy(true); + setOrderMsg(null); + setOrderErr(null); + setOrderDiff(null); + fetch(api("/dsh-market/bundle-order"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ order: target ?? order }) + }).then(async (res) => { + const body = await res.json().catch(() => null); + if (!res.ok || body?.ok !== true) { + const diff = body?.trial?.diff; + const overrides = diff?.overrides?.length ?? 0; + const orphans = diff?.orphans?.length ?? 0; + const duplicates = diff?.duplicates?.length ?? 0; + setOrderDiff(overrides + orphans + duplicates > 0 ? { + overrides, + orphans, + duplicates + } : null); + const firstMessage = body?.trial?.errors?.[0]?.message; + setOrderErr(body?.trial !== void 0 ? t("orderTrialFail").replace("{0}", firstMessage !== void 0 ? String(firstMessage) : "") : String(body?.error ?? `HTTP ${String(res.status)}`)); + return; + } + setOrderDiff(null); + setOrderMsg(t("orderApplied")); + refresh(); + }).catch((err) => setOrderErr(err instanceof Error ? err.message : String(err))).finally(() => setOrderBusy(false)); + }; + (0, react.useEffect)(() => { + let live = true; + setError(null); + fetch(api("/dsh-market/check"), { cache: "no-store" }).then(async (res) => { + if (!res.ok) throw new Error(`HTTP ${String(res.status)}`); + const body = await res.json(); + if (live) setReport(body); + }).catch((err) => { + if (live) setError(err instanceof Error ? err.message : String(err)); + }); + return () => { + live = false; + }; + }, [version]); + if (error !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.err, + children: [t("checkLoadFail"), error] + }); + if (report === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.loading, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spin, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutline16, { size: 22 }) + }), t("checkLoading")] + }); + const summary = report.summary; + const suggested = report.suggestedOrder ?? null; + const peerRisk = report.peerMismatches.filter((peer) => peer.satisfied === false && peer.verdict?.kind === "risk"); + const peerWarning = report.peerMismatches.filter((peer) => peer.satisfied === false && (peer.verdict === void 0 || peer.verdict.kind === "warning")); + const peerInfo = report.peerMismatches.filter((peer) => peer.satisfied === false && peer.verdict?.kind === "none" || peer.satisfied === null); + const peerSatisfied = report.peerMismatches.filter((peer) => peer.satisfied === true); + const catConflict = report.duplicates.length; + const catRisk = peerRisk.length; + const catWarn = peerWarning.length; + const catInfo = peerInfo.length; + const catMulti = report.multiVersion.length; + const catOrder = report.orderConflicts?.length ?? 0; + const anyIssue = catConflict + catRisk + catWarn + catInfo + catMulti + catOrder > 0; + const hasHardIssues = summary.errors.length > 0 || report.duplicates.length > 0 || catRisk > 0; + /** + * Build the AI-fix prompt (structured errors / duplicates / peer risks / + * order conflicts + scope) and copy it to the clipboard. The user pastes it + * into a new conversation and decides whether to send — the agent never runs + * automatically. + * (A previous auto-open/prefill attempt was dropped: it was unreliable + * across host versions, so plain copy + toast is the contract.) + */ + const startAgentFix = () => { + const lines = []; + lines.push(t("aiFixIntro").replace("{0}", report.profile)); + lines.push(""); + lines.push(t("aiFixDetect")); + lines.push(""); + lines.push(t("aiFixIfSelf")); + lines.push(""); + const errorLines = summary.errors.filter((line) => !line.startsWith("duplicate loader entry id")); + if (errorLines.length > 0) { + lines.push(`${t("checkErrors")}:`); + for (const e of errorLines) lines.push(`- ${e}`); + lines.push(""); + } + if (report.duplicates.length > 0) { + lines.push(`${t("checkDuplicates")}:`); + for (const dup of report.duplicates) lines.push(`- ${dup.id} × ${dup.count} (${dup.layers.join(" / ")})`); + lines.push(""); + } + if (peerRisk.length > 0) { + lines.push(`${t("checkPeerRisk")}:`); + for (const peer of peerRisk) { + const direction = peer.verdict?.kind === "risk" && peer.verdict.risk.direction === "aboveMax" ? t("peerRiskAboveMax") : t("peerRiskBelowMin"); + lines.push(`- ${peer.plugin} → ${peer.name}@${peer.range} (${t("checkResolved")}: ${peer.resolved ?? "—"}, ${direction})`); + } + lines.push(""); + } + if ((report.orderConflicts ?? []).length > 0) { + lines.push(`${t("catOrder")}:`); + for (const c of report.orderConflicts ?? []) lines.push(`- ${c.name}: ${c.reason}`); + lines.push(""); + } + lines.push(t("aiFixScope")); + lines.push(""); + lines.push(t("aiFixConservative")); + const prompt = lines.join("\n"); + setFixMsg(null); + setFixFallback(null); + const fallback = () => setFixFallback(prompt); + if (typeof navigator.clipboard?.writeText === "function") navigator.clipboard.writeText(prompt).then(() => setFixMsg(t("aiFixCopied"))).catch(fallback); + else fallback(); + }; + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagPage, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagSummary, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: summary.ok ? Market_module_css_default.okState : Market_module_css_default.err, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: summary.ok ? "done" : "error", + size: 8 + }), summary.ok ? anyIssue ? t("checkIssues") : t("diagOkAll") : t("checkIssues")] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.diagSummaryItem, + title: t("checkDuplicates"), + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: "error", + size: 8 + }), + t("catConflict"), + ": ", + catConflict + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.diagSummaryItem, + title: t("checkPeerRisk"), + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: "error", + size: 8 + }), + t("catRisk"), + ": ", + catRisk + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.diagSummaryItem, + title: t("checkPeerWarning"), + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: "warning", + size: 8 + }), + t("catWarn"), + ": ", + catWarn + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.diagSummaryItem, + title: t("checkPeerInfoTier"), + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: "ongoing", + size: 8 + }), + t("catInfo"), + ": ", + catInfo + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.diagSummaryItem, + title: t("checkMultiVersion"), + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: "warning", + size: 8 + }), + t("checkMultiVersion"), + ": ", + catMulti + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.diagSummaryItem, + title: t("checkOrderTip"), + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: "warning", + size: 8 + }), + t("catOrder"), + ": ", + catOrder + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + hasHardIssues && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: startAgentFix, + title: t("aiFixHint"), + children: t("aiFix") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + "aria-label": t("checkRefresh"), + onClick: refresh, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline14, { size: 14 }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.diagSummaryMeta, + title: report.profile, + children: [ + t("checkProfile"), + ": ", + report.profile + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagSummaryMeta, + children: new Date(report.scannedAt).toLocaleString() + }) + ] + }), + fixMsg !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.okState, + children: fixMsg + }), + fixFallback !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.fixFallback, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.panelNote, + children: t("aiFixFail") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", { + readOnly: true, + rows: 10, + className: Market_module_css_default.fixFallbackText, + value: fixFallback, + onFocus: (e) => e.currentTarget.select() + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleSection, { + title: t("diagExplain"), + open: explainOpen, + onToggle: () => setExplainOpen((o) => !o), + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.panelNote, + children: t("diagExplainText") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagList, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("diagTermBundle") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("diagTermEntry") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("diagTermPeer") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("diagTermShadow") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("diagTermOrphan") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("diagTermOrder") + }) + ] + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkErrors"), + count: summary.errors.length, + empty: t("checkErrorsEmpty"), + overview: summary.errors.length > 0 ? summary.errors[0] : void 0, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: summary.errors.map((line, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.err, + children: line + }, i)) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkWarnings"), + count: summary.warnings.length, + empty: t("checkWarningsEmpty"), + overview: summary.warnings.length > 0 ? summary.warnings[0] : void 0, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: summary.warnings.map((line, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.warnLine, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: line }) + }, i)) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkBundles"), + count: report.bundles.length, + empty: t("checkBundlesEmpty"), + problem: false, + overview: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [ + t("checkOfficial"), + " × ", + report.bundles.filter((b) => b.kind === "official").length, + " · ", + t("checkCommunity"), + " × ", + report.bundles.filter((b) => b.kind === "community").length + ] }), + children: report.bundles.map((bundle, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagBundle, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagIndex, + children: i + 1 + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagArrow, + children: "→" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.nm, + children: bundle.name + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: bundle.kind === "official" ? Market_module_css_default.diagBadgeOfficial : Market_module_css_default.diagBadgeCommunity, + children: bundle.kind === "official" ? t("checkOfficial") : t("checkCommunity") + }), + bundle.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.err, + children: bundle.error + }), + bundle.parseError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.err, + children: [ + t("checkPatch"), + ": ", + bundle.parseError + ] + }) + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagMeta, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagKey, + children: t("checkSource") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.spec, + children: bundle.source + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagMeta, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagKey, + children: t("checkEntries") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.spec, + children: bundle.entries.length > 0 ? bundle.entries.join(", ") : "—" + })] + }), + bundle.directory !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagMeta, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagKey, + children: t("checkDir") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.spec, + children: bundle.directory + })] + }), + bundle.patchPath !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagMeta, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagKey, + children: t("checkPatch") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.spec, + children: bundle.patchPath + })] + }) + ] + }, bundle.name)) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkDuplicates"), + count: report.duplicates.length, + empty: t("checkDuplicatesEmpty"), + overview: report.duplicates.length > 0 ? `${report.duplicates[0]?.id} × ${report.duplicates[0]?.count}` : void 0, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: report.duplicates.map((dup) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.diagVal, + children: dup.id + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.err, + children: ["× ", dup.count] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + children: dup.layers.join(" / ") + }) + ] + }, dup.id)) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkPeerRisk"), + count: peerRisk.length, + empty: t("checkPeerRiskEmpty"), + overview: peerRisk.length > 0 ? `${peerRisk[0].plugin} → ${peerRisk[0].name}@${peerRisk[0].range}(${t("checkResolved")}: ${peerRisk[0].resolved ?? "—"})` : void 0, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: peerRisk.map((peer, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.diagVal, + children: peer.name + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.nm, + children: peer.plugin + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + t("checkRange"), + ": ", + peer.range + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + t("checkResolved"), + ": ", + peer.resolved ?? "—" + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagBadgeShadow, + children: peerRiskLabel(peer, t) + }) + ] + }, i)) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkPeerWarning"), + count: peerWarning.length, + empty: t("checkPeerWarningEmpty"), + overview: peerWarning.length > 0 ? `${peerWarning[0].plugin} → ${peerWarning[0].name}@${peerWarning[0].range}(${peerWarningLabel(peerWarning[0], t)})` : void 0, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: peerWarning.map((peer, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.diagVal, + children: peer.name + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.nm, + children: peer.plugin + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + t("checkRange"), + ": ", + peer.range + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + t("checkResolved"), + ": ", + peer.resolved ?? "—" + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagBadgeWarn, + children: peerWarningLabel(peer, t) + }) + ] + }, i)) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkPeerInfoTier"), + count: peerInfo.length, + empty: t("checkPeerInfoTierEmpty"), + problem: false, + overview: peerInfo.length > 0 ? `${peerInfo[0].plugin} → ${peerInfo[0].name}@${peerInfo[0].range}` : void 0, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: peerInfo.map((peer, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.diagVal, + children: peer.name + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.nm, + children: peer.plugin + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + t("checkRange"), + ": ", + peer.range + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + t("checkResolved"), + ": ", + peer.resolved ?? "—" + ] + }), + peer.satisfied === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagBadgeInfo, + children: t("checkUnknown") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagBadgeInfo, + children: t("peerInfoUnverified") + }) + ] + }, i)) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkPeerSatisfied"), + count: peerSatisfied.length, + empty: t("checkPeerSatisfiedEmpty"), + problem: false, + overview: peerSatisfied.length > 0 ? `${peerSatisfied[0].plugin} → ${peerSatisfied[0].name}@${peerSatisfied[0].range}` : void 0, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: peerSatisfied.map((peer, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.diagVal, + children: peer.name + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.nm, + children: peer.plugin + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + t("checkRange"), + ": ", + peer.range + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + t("checkResolved"), + ": ", + peer.resolved ?? "—" + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.okState, + children: t("checkSatisfied") + }) + ] + }, i)) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkMultiVersion"), + count: report.multiVersion.length, + empty: t("checkMultiEmpty"), + overview: report.multiVersion.length > 0 ? `${report.multiVersion[0]?.name}: ${report.multiVersion[0]?.versions.join(" / ")}` : void 0, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: report.multiVersion.map((mv) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.diagVal, + children: mv.name + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + children: mv.versions.join(" / ") + }), + mv.hoisted !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + t("checkHoisted"), + ": ", + mv.hoisted + ] + }) + ] + }, mv.name)) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkOverrides"), + count: report.overrides.length, + empty: t("checkOverridesEmpty"), + overview: report.overrides.length > 0 ? `${report.overrides[0]?.id} ← ${report.overrides[0]?.layer}` : void 0, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: report.overrides.map((ov, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.ovRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.diagVal, + children: ov.id + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.ovArrow, + children: "←" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.ovByTag, + children: ov.layer + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + children: t("checkOverridden") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.ovFrom, + children: ov.overriddenLayers.join(", ") + }) + ] + }, i)) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, { + title: t("checkOrphans"), + count: report.orphans.length, + empty: t("checkOrphansEmpty"), + overview: report.orphans.length > 0 ? `${report.orphans[0]?.id}(${t(orphanKindLabel(report.orphans[0]?.reason ?? ""))})` : void 0, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: report.orphans.map((orphan, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.orphRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.orphBadge, + children: t(orphanKindLabel(orphan.reason)) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.diagVal, + children: orphan.id + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.nm, + children: orphan.layer + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + children: orphan.reason + }) + ] + }, i)) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CollapsibleSection, { + title: t("orderSection"), + count: order.length, + open: orderOpen, + onToggle: () => setOrderOpen((o) => !o), + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.panelNote, + children: t("orderDragHint") + }), + report.orderConflicts !== void 0 && report.orderConflicts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagList, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagKey, + children: t("orderConflicts") + }), report.orderConflicts.map((conflict, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.warnLine, + children: [ + conflict.name, + " — ", + conflict.reason + ] + }, i))] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + style: { + display: "flex", + alignItems: "center", + gap: 8, + flexWrap: "wrap" + }, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: order.length === 0 || orderBusy, + onClick: () => applyOrder(), + children: orderBusy ? "…" : t("orderApply") + }), + suggested !== null && suggested.ok === true && suggested.order.join("\0") !== communityNames.join("\0") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: orderBusy, + onClick: () => applyOrder(suggested.order), + children: t("orderSuggestApply") + }), + suggested !== null && suggested.ok === true && suggested.order.join("\0") === communityNames.join("\0") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.okState, + children: t("orderAlreadyOptimal") + }), + order.join("\0") !== communityNames.join("\0") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + disabled: orderBusy, + onClick: () => setOrder(communityNames), + children: t("orderReset") + }), + orderMsg !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.okState, + children: orderMsg + }), + orderErr !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.err, + children: orderErr + }) + ] + }), + orderDiff !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.panelNote, + children: t("orderDiffHint").replace("{0}", String(orderDiff.overrides)).replace("{1}", String(orderDiff.orphans)).replace("{2}", String(orderDiff.duplicates)) + }), + suggested !== null && suggested.ok === false && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.warnLine, + children: [ + t("orderSuggestHint"), + " ⚠ ", + suggested.cycle.join(" → ") + ] + }), + report.duplicateNames !== void 0 && report.duplicateNames.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.diagList, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagKey, + children: t("duplicateNames") + }), report.duplicateNames.map((dup, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.panelNote, + children: [ + dup.name, + " × ", + dup.count, + " — ", + dup.layers.join(" / ") + ] + }, i))] + }), + order.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagEmpty, + children: "—" + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.diagList, + children: order.map((name, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + draggable: !orderBusy, + className: [ + Market_module_css_default.diagRow, + dragIndex === i ? Market_module_css_default.dragging : "", + dragOverIndex === i ? Market_module_css_default.dragOver : "" + ].filter(Boolean).join(" "), + onDragStart: onRowDragStart(i), + onDragOver: onRowDragOver(i), + onDragLeave: onRowDragLeave(i), + onDrop: onRowDrop(i), + onDragEnd: onRowDragEnd, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.dragHandle, + "aria-label": t("orderDrag"), + title: t("orderDrag"), + children: "⠿" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.diagIndex, + children: i + 1 + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.nm, + children: name + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + draggable: false, + "aria-label": t("orderUp"), + disabled: i === 0 || orderBusy, + onClick: () => moveBundle(i, -1), + children: t("orderUp") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + draggable: false, + "aria-label": t("orderDown"), + disabled: i >= order.length - 1 || orderBusy, + onClick: () => moveBundle(i, 1), + children: t("orderDown") + }) + ] + }, name)) + }) + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleSection, { + title: t("presetSection"), + open: presetOpen, + onToggle: () => setPresetOpen((o) => !o), + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PresetPanel, { + t, + open: presetOpen, + bundleOrder: order, + onRefresh: refresh + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapsibleSection, { + title: t("snapSection"), + open: snapOpen, + onToggle: () => setSnapOpen((o) => !o), + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SnapshotPanel, { + t, + open: snapOpen, + onRefresh: refresh + }) + }) + ] + }); + } + //#endregion + //#region src/client/self-check.ts + /** + * What the browser can see about the market that the server cannot. + * + * This exists because of a pattern, not a hypothesis. #293 ("市场显示空白") + * and #384 ("lightbox cannot be closed") are both reproducible for their + * reporters and for nobody else, on clean profiles, across host versions. + * Both investigations then stalled in the same place: I asked the reporter to + * open a console and paste the result of an expression. That costs a day per + * question, asks a non-developer to run code from a stranger, and is the one + * step where a report goes quiet. + * + * Meanwhile the exported log — which the issue template already asks for, and + * which reporters do send — describes only the server: dependencies, bundle + * resolution, events. Everything it says was already true of a machine where + * the bug does not happen. The interesting half was in a page nobody looked + * at. + * + * So the browser answers the questions I have actually had to ask, in the + * artefact people already know how to produce. Nothing here diagnoses + * anything on its own; it is evidence, collected before it is needed. + */ + /** + * How many times this module has been evaluated in this page. + * + * Module scope runs once per module instance, so >1 means the market's client + * bundle was loaded twice — two React copies, two of every module singleton, + * two portal containers. That is a specific hypothesis for #384: two React + * roots each attach delegated listeners to their own portal container, and a + * click lands on whichever container is last in `body` while the handlers + * live on the other one, so the buttons look present and do nothing. + * + * Counted on `window` rather than in a module variable for the obvious + * reason: a module variable would be duplicated along with everything else + * and each copy would confidently report 1. + */ + const globals = globalThis; + globals.__dshmarketClientLoads = (globals.__dshmarketClientLoads ?? 0) + 1; + /** Whether `value` looks like a browser environment worth inspecting. */ + const hasDom = () => typeof document !== "undefined" && document.body !== null; + /** + * The browser-side section of the exported log. + * + * Deliberately facts, not verdicts. "portal containers: 2" is something a + * reporter can paste without judging it, and something I can act on; "your + * bundle is double-loaded" would be a guess printed in the user's face, and + * wrong the first time a host legitimately renders two markets. + * @returns the lines to append, or an empty array outside a browser. + */ + function clientDiagnostics() { + if (!hasDom()) return []; + const portals = document.querySelectorAll("[data-dsh-market-portal]"); + const roots = document.querySelectorAll("[data-dsh-market-root]"); + const last = portals.length > 0 ? portals[portals.length - 1] : null; + return [ + `client bundle evaluations: ${String(globals.__dshmarketClientLoads ?? 0)}`, + `market roots in the document: ${String(roots.length)}`, + `portal containers: ${String(portals.length)}` + (last === null ? "" : ` (last one is body's last child: ${String(last === document.body.lastElementChild)})`), + `plugin cards rendered: ${String(document.querySelectorAll("[data-dsh-market-root] [class*=\"_card\"]").length)}`, + `document baseURI: ${document.baseURI}`, + `page URL: ${location.origin}${location.pathname}`, + `user agent: ${navigator.userAgent}` + ]; + } + //#endregion + //#region src/client/MarketSection.tsx + /** + * The Market settings section: Discover / Themes / Installed tabs over the + * /dsh-market/* host routes, with install/update/uninstall flows and the + * pending-restart bookkeeping in sessionStorage. + */ + function isHostDependencyFinding(value) { + if (value === null || typeof value !== "object") return false; + const finding = value; + return finding.code === "shared-host-package-dependency" && finding.severity === "warning" && finding.subject?.kind === "package" && typeof finding.subject.name === "string" && finding.evidence?.basis === "manifest-declaration" && typeof finding.evidence?.dependency === "string" && typeof finding.evidence.declaredRange === "string" && finding.evidence.declaredIn === "dependencies"; + } + const HOST_DEPENDENCY_PREVIEW_LIMIT = 5; + const IGNORED_UPDATES_SESSION_KEY = "dshm-updates-ignored"; + /** + * Read the update reminders dismissed for this host process. The boot id is + * part of the value rather than the key so sessionStorage never accumulates + * one orphaned entry per process. Invalid and stale records fail open: an + * update reminder is safer than silently hiding one we cannot account for. + */ + function ignoredUpdatesForBoot(boot) { + const saved = readSession(IGNORED_UPDATES_SESSION_KEY); + if (!(saved !== null && typeof saved === "object" && !Array.isArray(saved) && saved.boot === boot && Array.isArray(saved.names) && saved.names.every((name) => typeof name === "string" && name !== ""))) { + try { + sessionStorage.removeItem(IGNORED_UPDATES_SESSION_KEY); + } catch {} + return []; + } + return [...new Set(saved.names)]; + } + function HostDependencyDiagnostics({ findings, t }) { + if (findings.length === 0) return null; + const preview = findings.slice(0, HOST_DEPENDENCY_PREVIEW_LIMIT); + const remaining = findings.length - preview.length; + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.banner, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { + size: 14, + className: Market_module_css_default.bannerIcon + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.grow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("hostDependencyWarning") }), + preview.map((finding) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.spec, + children: [ + finding.subject.name, + " → ", + finding.evidence.dependency, + "@", + finding.evidence.declaredRange + ] + }, `${finding.subject.name}:${finding.evidence.dependency}`)), + remaining > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("hostDependencyMore").replace("{0}", String(remaining)) + }) + ] + })] + }); + } + /** The state label + dot for one activation result (P0-2). */ + function activationMeta(state, t) { + if (state === "live") return { + label: t("stateLive"), + dot: "done" + }; + if (state === "restart") return { + label: t("stateRestart"), + dot: "warning" + }; + if (state === "inert") return { + label: t("stateInert"), + dot: "warning" + }; + if (state === "broken") return { + label: t("stateBroken"), + dot: "error" + }; + if (state === "disabled") return { + label: t("stateDisabled"), + dot: "warning" + }; + return { + label: "—", + dot: "warning" + }; + } + function phaseLabel(phase, t) { + if (phase === "resolving") return t("phaseResolving"); + if (phase === "downloading") return t("phaseDownloading"); + if (phase === "linking") return t("phaseLinking"); + return t("phaseBuilding"); + } + /** + * Page/page-size state shared by every paged list in this file (Discover, + * Themes) — each caller owns its OWN instance (their filters are + * independent, a search in one tab has no business resetting the other's + * page), but the mechanics (clamp against a shrinking list, reset to page 1 + * when the filters that produced `count` change, scroll back to the top of + * the shared body on any page move) are one implementation, not two. + */ + function usePagination(count, resetDeps, scrollToTop) { + const [page, setPage] = (0, react.useState)(1); + const [pageSize, setPageSize] = (0, react.useState)(DEFAULT_PAGE_SIZE); + (0, react.useEffect)(() => { + setPage(1); + }, resetDeps); + const totalPages = Math.max(1, Math.ceil(count / pageSize)); + const currentPage = Math.min(page, totalPages); + const goToPage = (next) => { + setPage(Math.max(1, Math.min(next, totalPages))); + scrollToTop(); + }; + const changePageSize = (size) => { + setPageSize(size); + setPage(1); + scrollToTop(); + }; + return { + currentPage, + totalPages, + pageSize, + goToPage, + changePageSize + }; + } + /** + * The sort/time-range dropdown (primitives Menu): three independent option + * groups, ids namespaced so one onSelect routes by prefix. Owns its own + * open state — a caller wires only the sort VALUES, not the dropdown's UI + * state, so Discover and Themes can each mount one without threading an + * extra `filterOpen`/`setFilterOpen` pair through their own state. + */ + function FilterMenu({ sortField, sortDir, timeRange, onSortField, onSortDir, onTimeRange, t }) { + const [open, setOpen] = (0, react.useState)(false); + const sortDirLabel = (dir) => sortField === "added" ? dir === "desc" ? "sortNewest" : "sortOldest" : dir === "desc" ? "sortDesc" : "sortAsc"; + const items = (0, react.useMemo)(() => [ + { + type: "label", + id: "f-sort", + text: t("filterSort") + }, + ...SORT_FIELD_OPTIONS.map((opt) => ({ + id: "field:" + opt.key, + label: t(opt.label) + })), + { + type: "separator", + id: "f-sep1" + }, + { + type: "label", + id: "f-dir", + text: t("filterDir") + }, + ...SORT_DIR_OPTIONS.map((dir) => ({ + id: "dir:" + dir, + label: t(sortDirLabel(dir)) + })), + { + type: "separator", + id: "f-sep2" + }, + { + type: "label", + id: "f-time", + text: t("filterTime") + }, + ...TIME_OPTIONS.map((opt) => ({ + id: "time:" + opt.key, + label: t(opt.label) + })) + ], [t, sortField]); + const selectedIds = (0, react.useMemo)(() => [ + "field:" + sortField, + "dir:" + sortDir, + "time:" + timeRange + ], [ + sortField, + sortDir, + timeRange + ]); + const onSelect = (id) => { + if (id.startsWith("field:")) onSortField(id.slice(6)); + else if (id.startsWith("dir:")) onSortDir(id.slice(4)); + else if (id.startsWith("time:")) onTimeRange(id.slice(5)); + }; + return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, { + open, + onClose: () => setOpen(false), + onSelect, + selectedIds, + align: "end", + portal: true, + anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + icon: open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }), + onClick: () => setOpen((o) => !o), + children: t("filter") + }), + items + }); + } + /** First/prev/numbered/next/last controls plus a per-page-size menu — one + * implementation for every paged list, driven entirely by `usePagination`'s + * return value. Owns its own page-size dropdown open state for the same + * reason `FilterMenu` owns its own. */ + function Pager({ currentPage, totalPages, pageSize, onGoToPage, onChangePageSize, t }) { + const [sizeOpen, setSizeOpen] = (0, react.useState)(false); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.pager, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.pagerPages, + children: totalPages > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: currentPage === 1, + onClick: () => onGoToPage(1), + "aria-label": t("firstPage"), + children: "«" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronLeftOutline14, { size: 14 }), + disabled: currentPage === 1, + onClick: () => onGoToPage(currentPage - 1), + children: t("prevPage") + }), + pageItems(currentPage, totalPages).map((item, i) => item === "…" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.pageEllipsis, + children: "…" + }, "e" + i) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: item === currentPage ? "primary" : "outline", + size: "sm", + onClick: () => onGoToPage(item), + children: item + }, item)), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: currentPage === totalPages, + onClick: () => onGoToPage(currentPage + 1), + children: [t("nextPage"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { size: 14 })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: currentPage === totalPages, + onClick: () => onGoToPage(totalPages), + "aria-label": t("lastPage"), + children: "»" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.pageInfo, + children: t("pageInfo").replace("{0}", String(currentPage)).replace("{1}", String(totalPages)) + }) + ] }) + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, { + open: sizeOpen, + onClose: () => setSizeOpen(false), + onSelect: (id) => onChangePageSize(Number(id)), + selectedId: String(pageSize), + align: "end", + portal: true, + anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }), + onClick: () => setSizeOpen((o) => !o), + children: t("perPage") + " " + pageSize + }), + items: PAGE_SIZES.map((size) => ({ + id: String(size), + label: String(size) + })) + })] + }); + } + /** + * Card avatar: the plugin owner's GitHub avatar (no API, browser-cached), + * falling back to the initial-letter tile when it can't load. + */ + /** Inline pass: `code` spans and **bold**, everything else plain text. */ + function mdInline(text) { + return text.split(/(\*\*[^*]+\*\*|`[^`]+`)/g).map((part, i) => { + if (part.startsWith("**") && part.endsWith("**") && part.length > 4) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: part.slice(2, -2) }, i); + if (part.startsWith("`") && part.endsWith("`") && part.length > 2) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.notesCode, + children: part.slice(1, -1) + }, i); + return part; + }); + } + /** + * Release-body markdown, reduced to what a reading dialog needs: headings, + * bullets, paragraphs, bold, inline code. Every character arrives as a React + * text child (auto-escaped) — nothing from the repo is ever interpreted as + * markup, so this stays free of the HTML surface real markdown parsers open. + */ + function renderMarkdown(md) { + const out = []; + let bullets = null; + const flushList = () => { + if (bullets === null) return; + const items = bullets; + out.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { + className: Market_module_css_default.notesList, + children: items.map((item, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: mdInline(item) }, i)) + }, `l${out.length}`)); + bullets = null; + }; + for (const line of md.split("\n")) { + const trimmed = line.trim(); + if (trimmed === "") { + flushList(); + continue; + } + const heading = /^#{1,6}\s+(.*)$/.exec(trimmed); + if (heading !== null) { + flushList(); + out.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.notesH, + children: mdInline(heading[1]) + }, `h${out.length}`)); + continue; + } + const bullet = /^[-*]\s+(.*)$/.exec(trimmed); + if (bullet !== null) { + (bullets ??= []).push(bullet[1]); + continue; + } + flushList(); + out.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.notesP, + children: mdInline(line) + }, `p${out.length}`)); + } + flushList(); + return out; + } + function OwnerAvatar({ name, owner }) { + const [failed, setFailed] = (0, react.useState)(false); + if (failed || owner === "") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.av, + style: { background: avatarColor(name) }, + children: name.replace(/^dsh[-_]/i, "").charAt(0).toUpperCase() || "P" + }); + return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", { + className: Market_module_css_default.av, + src: avatarUrl(owner), + alt: "", + loading: "lazy", + onError: () => setFailed(true) + }); + } + /** + * AppStore-style screenshot strip in the install detail dialog (#61). + * Curated registry screenshots win; otherwise images are extracted from the + * repo README. Requests start only once the dialog opens; failures — no + * README, no images, broken links — degrade to rendering nothing at all. + */ + function ScreenshotStrip({ plugin, onOpen }) { + const [shots, setShots] = (0, react.useState)([]); + const [broken, setBroken] = (0, react.useState)([]); + (0, react.useEffect)(() => { + let live = true; + setShots([]); + setBroken([]); + pluginScreenshots(plugin).then((list) => { + if (live) setShots(list); + }); + return () => { + live = false; + }; + }, [plugin]); + const visible = shots.filter((src) => !broken.includes(src)); + if (visible.length === 0) return null; + return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.shots, + children: visible.map((src, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", { + className: Market_module_css_default.shot, + src: thumbUrl(src, 300), + alt: "", + loading: "lazy", + decoding: "async", + referrerPolicy: "no-referrer", + onClick: () => onOpen(visible, i), + onError: () => setBroken((prev) => prev.includes(src) ? prev : prev.concat(src)) + }, src)) + }); + } + /** + * Advances an index every `intervalMs` while `count > 1` — the shared clock + * behind both a card's auto-cycling thumbnail and the lightbox. A manual + * jump (clicking a dot, an arrow, opening on a specific shot) restarts the + * clock instead of letting it fire again moments later: without that, a + * deliberate "go back one" reads as broken when it auto-advances right past + * where the user just navigated to. + * + * `intervalMs <= 0` disables the timer entirely (no auto-advance at all); + * manual jumps still work. The lightbox uses this: a full-bleed image needs + * to stay put until the viewer moves on, so it must never page itself. + */ + function useAutoCarousel(count, initial, intervalMs = 3500) { + const [index, setIndexState] = (0, react.useState)(initial); + const [resetTick, setResetTick] = (0, react.useState)(0); + (0, react.useEffect)(() => { + if (count <= 1 || intervalMs <= 0) return; + const timer = setInterval(() => { + setIndexState((i) => (i + 1) % count); + }, intervalMs); + return () => clearInterval(timer); + }, [ + count, + intervalMs, + resetTick + ]); + const setIndex = (i) => { + if (count <= 0) return; + setIndexState((i % count + count) % count); + setResetTick((t) => t + 1); + }; + return [index, setIndex]; + } + /** + * A card thumbnail (or dialog strip image) renders at well under 150px on + * screen; the curated screenshot behind it can be a full-resolution PNG + * several hundred KB to a few MB — GitHub's own hosts offer no resized + * variant, so rendering the original meant downloading full-size images for + * a strip nobody asked to see full-size. images.weserv.nl resizes + * server-side (by decoded HEIGHT, `fit=inside` so it never crops, `we=1` so + * it never upscales something already smaller) before the bytes reach the + * browser. The lightbox — an explicit "show me this big" — still requests + * the ORIGINAL directly: proxying that one too would add a hop with nothing + * left to save, and once the thumbnail is genuinely smaller it can no longer + * share a cache entry with the full-size open anyway. + */ + function thumbUrl(src, height) { + return `https://images.weserv.nl/?url=${encodeURIComponent(src.replace(/^https?:\/\//, ""))}&h=${String(height)}&fit=inside&we=1`; + } + /** + * The owner's GitHub avatar, addressed so the region's proxy can serve it. + * + * `github.com/.png` is a redirect to the avatar host, and gh-proxy + * does not follow it — measured from an unproxied mainland connection, that + * URL hangs until the client gives up (60s), while naming the avatar host + * directly through the same proxy answers in 1.07s. So a proxied region + * addresses the destination itself. + * + * The redirect is left in place when there is no proxy: it is the form that + * has always worked, and this is not the release to change it on a path + * nobody has reported a problem with. + */ + function avatarUrl(owner) { + const name = encodeURIComponent(owner); + return githubProxyInUse() === null ? `https://github.com/${name}.png?size=96` : githubUrl(`https://avatars.githubusercontent.com/${name}?size=96`); + } + /** + * True once the wrapped element has scrolled within `rootMargin` of the + * viewport. Falls back to true immediately where IntersectionObserver is + * unavailable (old browsers, jsdom without a stub) — a missing observer + * should degrade to eager loading, not a permanently empty thumbnail. + * Native `img loading="lazy"` already defers the network fetch on its own, + * but its trigger distance isn't ours to tune, and scrolling a 400+ entry + * catalog queues every off-screen card's request the instant the browser + * decides to start prefetching — this hook is what lets CardShot not even + * SET `src` until a card is actually close. + */ + function useNearViewport(rootMargin = "200px") { + const [near, setNear] = (0, react.useState)(typeof IntersectionObserver === "undefined"); + const [node, setNode] = (0, react.useState)(null); + (0, react.useEffect)(() => { + if (near || node === null) return; + const obs = new IntersectionObserver((entries) => { + if (entries.some((entry) => entry.isIntersecting)) setNear(true); + }, { rootMargin }); + obs.observe(node); + return () => obs.disconnect(); + }, [ + near, + node, + rootMargin + ]); + return [setNode, near]; + } + /** + * A card's own thumbnail strip — curated screenshots only (#61 supplement): + * this data already rode along with the catalog fetch that drew the grid, + * so showing it costs nothing extra. README-scraped fallback images stay + * dialog-only, where fetching one repo's README on click is a single + * request instead of one per visible card. + * + * Horizontal scroll at each image's own aspect ratio, not an auto-cycling + * single crop: cropping every shot into one fixed box hid most of a tall + * screenshot, and cycling on a timer meant the card you were looking at + * kept changing under you. Scrolling is a gesture the user drives. + */ + /** Thumbnails per card. The dialog shows every screenshot; a grid of cards + * pulling six full-size PNGs each is what makes the first paint crawl. */ + const CARD_SHOT_LIMIT = 3; + function CardShot({ plugin, onOpen }) { + const shots = safeScreenshots(plugin.screenshots); + const [broken, setBroken] = (0, react.useState)([]); + const visible = shots.filter((src) => !broken.includes(src)).slice(0, CARD_SHOT_LIMIT); + const [setStripRef, near] = useNearViewport(); + if (visible.length === 0) return null; + return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + ref: setStripRef, + className: Market_module_css_default.cardShots, + children: visible.map((src, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", { + className: Market_module_css_default.cardShot, + src: near ? thumbUrl(src, 200) : void 0, + alt: "", + loading: "lazy", + decoding: "async", + fetchPriority: "low", + referrerPolicy: "no-referrer", + onClick: (e) => { + e.stopPropagation(); + onOpen(visible, i); + }, + onError: () => setBroken((prev) => prev.includes(src) ? prev : prev.concat(src)) + }, src)) + }); + } + /** + * Read dimensions through the same low-resolution, no-upscale route used by + * card thumbnails. Large originals therefore stay off the wire, while a + * genuinely tiny image remains tiny and can be rejected by the scorer. + */ + function measureThemeCandidates(candidates) { + if (typeof Image === "undefined") return Promise.resolve([]); + return Promise.all(candidates.map((candidate) => new Promise((resolve) => { + const probe = new Image(); + let settled = false; + const finish = (measurement) => { + if (settled) return; + settled = true; + window.clearTimeout(timer); + probe.onload = null; + probe.onerror = null; + resolve(measurement); + }; + const timer = window.setTimeout(() => finish(null), 6e3); + probe.onload = () => finish({ + src: candidate.src, + width: probe.naturalWidth, + height: probe.naturalHeight + }); + probe.onerror = () => finish(null); + probe.referrerPolicy = "no-referrer"; + probe.decoding = "async"; + probe.src = thumbUrl(candidate.src, 240); + }))).then((results) => results.filter((result) => result !== null)); + } + const measuredThemePreviewTasks = /* @__PURE__ */ new Map(); + const measuredThemePreviewResults = /* @__PURE__ */ new Map(); + /** README fetch + geometry probes, shared across search/page remounts. */ + function measuredThemePreview(plugin) { + const cached = measuredThemePreviewTasks.get(plugin.url); + if (cached !== void 0) return cached; + const task = pluginScreenshotCandidates(plugin).then(async (candidates) => { + const ranked = rankThemeScreenshots(candidates, await measureThemeCandidates(candidates)); + measuredThemePreviewResults.set(plugin.url, ranked); + return ranked; + }).catch(() => { + measuredThemePreviewResults.set(plugin.url, []); + return []; + }); + measuredThemePreviewTasks.set(plugin.url, task); + return task; + } + /** + * Themes are chosen visually, so their catalog card gets one stable, large + * preview instead of the generic plugin card's horizontal thumbnail strip. + * Curated screenshots keep their declared order. A missing curated set is + * filled lazily from README only when the card nears the viewport, then + * ranked by both README semantics and measured image geometry. + */ + function ThemeCover({ plugin, onOpen, t }) { + const curated = safeScreenshots(plugin.screenshots); + const curatedKey = curated.join("\n"); + const cachedFallback = curated.length === 0 ? measuredThemePreviewResults.get(plugin.url) : void 0; + const [fallback, setFallback] = (0, react.useState)({ + loading: curated.length === 0 && cachedFallback === void 0, + shots: cachedFallback ?? [] + }); + const [broken, setBroken] = (0, react.useState)([]); + const [setCoverRef, near] = useNearViewport(); + (0, react.useEffect)(() => { + setBroken([]); + if (curated.length > 0) { + setFallback({ + loading: false, + shots: [] + }); + return; + } + const cached = measuredThemePreviewResults.get(plugin.url); + if (cached !== void 0) { + setFallback({ + loading: false, + shots: cached + }); + return; + } + setFallback({ + loading: true, + shots: [] + }); + if (!near) return; + let live = true; + measuredThemePreview(plugin).then((shots) => { + if (live) setFallback({ + loading: false, + shots + }); + }); + return () => { + live = false; + }; + }, [ + curatedKey, + near, + plugin.url + ]); + const visible = (curated.length > 0 ? curated : fallback.shots).filter((src) => !broken.includes(src)); + const name = pluginName(plugin.name); + if (visible.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { + ref: setCoverRef, + type: "button", + className: `${Market_module_css_default.themeCover} ${Market_module_css_default.themeCoverEmpty}`, + "aria-label": `${name}: ${fallback.loading ? t("themePreviewLoading") : t("themePreviewMissing")}`, + disabled: true, + children: [fallback.loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spin, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutline16, { size: 20 }) + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSparkle16, { size: 20 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: fallback.loading ? t("themePreviewLoading") : t("themePreviewMissing") })] + }); + const src = visible[0]; + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { + ref: setCoverRef, + type: "button", + className: Market_module_css_default.themeCover, + "aria-label": `${t("themePreview")} ${name}`, + onClick: () => onOpen(visible, 0), + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", { + src: near ? thumbUrl(src, 520) : void 0, + alt: "", + loading: "lazy", + decoding: "async", + fetchPriority: "low", + referrerPolicy: "no-referrer", + onError: () => setBroken((prev) => prev.includes(src) ? prev : prev.concat(src)) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.themePreviewAction, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 }), t("themePreview")] + }), + visible.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.themePreviewCount, + children: t("themePreviewCount").replace("{0}", String(visible.length)) + }) + ] + }); + } + /** + * Masonry columns holding items in their input order. + * + * Items are dealt alternately (0,2,4… left; 1,3,5… right) rather than split + * down the middle, so the sort order still reads left-to-right then down — + * the ranking is the whole point of the sort menu above it. Each column is + * its own flex stack, so a tall item only pushes down the items beneath IT + * instead of leaving a hole beside its shorter neighbour. + * + * Below the two-up breakpoint the CSS collapses to one column, and dealing + * alternately would then interleave the list wrongly — so at one column the + * items stay in a single stack in their original order. + */ + function Masonry({ items, render, columns = 2 }) { + if (!useMediaWide() || columns < 2) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.masonry, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.masonryCol, + children: items.map(render) + }) + }); + const buckets = Array.from({ length: columns }, () => []); + items.forEach((item, index) => { + buckets[index % columns].push(item); + }); + return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.masonry, + children: buckets.map((bucket, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.masonryCol, + children: bucket.map(render) + }, index)) + }); + } + /** + * Whether the layout is at its two-up width. Matches the CSS breakpoint + * exactly: the column split is decided in JS but rendered by CSS, and the + * two disagreeing would deal cards into columns the stylesheet has already + * stacked. + */ + function useMediaWide() { + const query = "(min-width: 681px)"; + const subscribe = (0, react.useCallback)((notify) => { + if (typeof matchMedia !== "function") return () => {}; + const list = matchMedia(query); + list.addEventListener("change", notify); + return () => list.removeEventListener("change", notify); + }, []); + return (0, react.useSyncExternalStore)(subscribe, () => typeof matchMedia === "function" ? matchMedia(query).matches : true, () => true); + } + /** + * A card's description, clamped to 5 lines so one wordy entry doesn't blow + * the two-up grid's row height out for whatever sits beside it — the grid + * already tolerates SOME height variance by design (`.card`'s `align-self: + * start`), just not an unbounded one. The toggle only renders when the text + * actually overflows the clamp: a two-line description has nothing to + * "expand", so no button beats a button that does nothing. + */ + function CardDesc({ text, t }) { + const ref = (0, react.useRef)(null); + const [expanded, setExpanded] = (0, react.useState)(false); + const [canExpand, setCanExpand] = (0, react.useState)(false); + (0, react.useLayoutEffect)(() => { + const el = ref.current; + if (el === null) return; + setCanExpand(el.scrollHeight > el.clientHeight + 1); + }, [text]); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + ref, + className: expanded ? Market_module_css_default.desc : `${Market_module_css_default.desc} ${Market_module_css_default.descClamp}`, + children: text + }), canExpand && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + className: Market_module_css_default.descToggle, + "aria-label": expanded ? t("descCollapse") : t("descExpand"), + onClick: () => setExpanded((e) => !e), + children: expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }) + })] }); + } + /** + * Full-bleed image preview, opened from a card thumbnail or a dialog's + * screenshot strip. Not the shared Modal primitive: Modal is chrome for a + * decision (title, description, footer actions); this is just the same + * already-downloaded image shown bigger — there is no separate "thumbnail" + * vs "full size" asset to fetch. + */ + function ScreenshotLightbox({ shots, startIndex, onClose, t }) { + const [index, setIndex] = useAutoCarousel(shots.length, startIndex, 0); + const host = useMarketPortalHost(); + (0, react.useEffect)(() => { + const onKey = (e) => { + if (e.key === "Escape") { + e.stopPropagation(); + onClose(); + } else if (e.key === "ArrowLeft") { + e.stopPropagation(); + setIndex(index - 1); + } else if (e.key === "ArrowRight") { + e.stopPropagation(); + setIndex(index + 1); + } + }; + window.addEventListener("keydown", onKey, true); + return () => window.removeEventListener("keydown", onKey, true); + }, [index]); + return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.lightbox, + onClick: onClose, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + className: Market_module_css_default.lightboxClose, + "aria-label": t("lightboxClose"), + onClick: onClose, + children: "×" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", { + className: Market_module_css_default.lightboxImg, + src: shots[index], + alt: "", + onClick: (e) => e.stopPropagation() + }), + shots.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + className: `${Market_module_css_default.lightboxNav} ${Market_module_css_default.lightboxPrev}`, + "aria-label": t("lightboxPrev"), + onClick: (e) => { + e.stopPropagation(); + setIndex(index - 1); + }, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronLeftOutline14, { size: 18 }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + className: `${Market_module_css_default.lightboxNav} ${Market_module_css_default.lightboxNext}`, + "aria-label": t("lightboxNext"), + onClick: (e) => { + e.stopPropagation(); + setIndex(index + 1); + }, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { size: 18 }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.lightboxDots, + onClick: (e) => e.stopPropagation(), + children: shots.map((src, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: i === index ? `${Market_module_css_default.lightboxDot} ${Market_module_css_default.lightboxDotOn}` : Market_module_css_default.lightboxDot, + onClick: () => setIndex(i) + }, src)) + }) + ] }) + ] + }), host); + } + /** + * The one DOM node this package portals into, created on first use and kept + * for the life of the page. + * + * Created imperatively rather than rendered, and never removed: the point is + * that `document.body`'s child list stops being shared state between two + * React roots. A container that came and went would put the same churn back + * into body, just less often — and "less often" is what made this bug + * intermittent and hard to believe in the first place. + * + * Re-appended on every open so it stays last among body's children. That is + * what keeps the lightbox above the host's own portalled dialog, which is + * why the portal exists at all; moving a node we own is not something the + * host's root tracks, so it cannot disturb it. + */ + let portalHost = null; + function marketPortalHost() { + if (portalHost === null) { + portalHost = document.createElement("div"); + portalHost.setAttribute("data-dsh-market-portal", ""); + } + return portalHost; + } + /** + * Move the container to the end of `document.body`, which is what keeps this + * package's layers above the host's own portalled dialog. + * + * In a layout effect, NOT during render. `createPortal` needs the element + * while rendering, but appending it does not belong there: React may start a + * render, abandon it and start again, so a mutation in the render body runs + * for passes that never commit — and this particular mutation reorders + * `document.body`, the one container this package shares with the host's + * separate React root. That is the same shared-child-list hazard #293 was + * about, just arrived at from the other side. Committing it in an effect + * means it happens once, after React is done, in the order React expects. + */ + function useMarketPortalHost() { + const host = marketPortalHost(); + (0, react.useLayoutEffect)(() => { + document.body.appendChild(host); + }, [host]); + return host; + } + /** + * Official-style market glyph: the shared block-grid brand mark converted to + * the official monochrome icon form (16×16, fill="currentColor") so it + * follows the active theme. Mirrors the settings-nav glyph used for the + * "market" section id. + */ + function MarketLogo({ size = 16, style, animated = false }) { + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", { + width: size, + height: size, + viewBox: "0 0 16 16", + fill: "none", + xmlns: "http://www.w3.org/2000/svg", + "aria-hidden": "true", + style, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", { + fill: "currentColor", + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", { + x: "1.96", + y: "3.36", + width: "3.3", + height: "3.3", + rx: "0.53" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", { + x: "5.71", + y: "3.36", + width: "3.3", + height: "3.3", + rx: "0.53" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", { + x: "1.96", + y: "7.11", + width: "3.3", + height: "3.3", + rx: "0.53" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", { + x: "5.71", + y: "7.11", + width: "3.3", + height: "3.3", + rx: "0.53" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", { + x: "9.46", + y: "7.11", + width: "3.3", + height: "3.3", + rx: "0.53" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", { + x: "1.96", + y: "10.86", + width: "3.3", + height: "3.3", + rx: "0.53" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", { + x: "5.71", + y: "10.86", + width: "3.3", + height: "3.3", + rx: "0.53" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", { + x: "9.46", + y: "10.86", + width: "3.3", + height: "3.3", + rx: "0.53" + }) + ] + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", { + className: animated ? Market_module_css_default.logoPlug : void 0, + x: "10.74", + y: "2.09", + width: "3.3", + height: "3.3", + rx: "0.53", + fill: "currentColor", + transform: animated ? void 0 : "rotate(9 12.39 3.74)" + })] + }); + } + /** + * GitHub mark beside catalog card titles (#256, #365). Catalog intake in + * awesome-dsh-plugin rejects any entry whose `url` is not + * `https://github.com/owner/repo` (scripts/lib/entries.mjs), so this renders + * unconditionally — not a bet that today's snapshot happens to be all + * GitHub. The generic outbound arrow did not say so until hover. This rides + * the title's own line — no second link, no extra row. + */ + function GithubRepoMark({ size = 12, className }) { + return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", { + width: size, + height: size, + viewBox: "0 0 16 16", + fill: "currentColor", + xmlns: "http://www.w3.org/2000/svg", + "aria-hidden": "true", + className, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { + fillRule: "evenodd", + d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" + }) + }); + } + /** + * Module-scope caches so re-entering the section renders instantly instead + * of refetching and rebuilding from a spinner (#30 by @StarsTom). Module + * state survives section switches; a background refetch keeps it current. + */ + let cachedRegistry = null; + let cachedInstalled = null; + let cachedRepoIdentities = null; + let cachedRepoHints = null; + /** Discover grid page-size choices — the catalog grows daily, so cap each page. */ + const PAGE_SIZES = [ + 24, + 48, + 96 + ]; + const DEFAULT_PAGE_SIZE = 24; + const WEBDAV_STORAGE_KEY = "dshm-webdav"; + function savedWebdav() { + try { + const value = JSON.parse(localStorage.getItem(WEBDAV_STORAGE_KEY) ?? "{}"); + return { + url: typeof value.url === "string" ? value.url : "", + username: typeof value.username === "string" ? value.username : "", + password: "", + auto: value.auto === true + }; + } catch { + return { + url: "", + username: "", + password: "", + auto: false + }; + } + } + function backupDependencies(value) { + if (value === null || typeof value !== "object") throw new Error("invalid backup"); + const backup = value; + if (backup.format !== "dsh-profile-backup" || backup.version !== .2) throw new Error("unsupported backup format"); + const files = backup.files; + if (!Array.isArray(files)) throw new Error("unsupported backup format"); + const manifest = files.find((file) => file !== null && typeof file === "object" && file.path === "package.json"); + if (manifest?.json === null || typeof manifest?.json !== "object" || Array.isArray(manifest.json)) throw new Error("backup package.json is invalid"); + const dependencies = manifest.json.dependencies; + if (dependencies === null || typeof dependencies !== "object" || Array.isArray(dependencies)) return {}; + if (!Object.values(dependencies).every((spec) => typeof spec === "string")) throw new Error("backup dependencies are invalid"); + return dependencies; + } + function installedRepoIdentities(value) { + if (value === null || typeof value !== "object" || Array.isArray(value)) return {}; + const identities = {}; + for (const [name, ids] of Object.entries(value)) { + if (!Array.isArray(ids)) continue; + const strings = ids.filter((id) => typeof id === "string"); + if (strings.length > 0) identities[name] = strings; + } + return identities; + } + function installedRepoHints(value) { + return installedRepoIdentities(value); + } + function installedMap(value) { + if (value === null || typeof value !== "object" || Array.isArray(value)) return {}; + const installed = {}; + for (const [name, spec] of Object.entries(value)) if (typeof spec === "string") installed[name] = spec; + return installed; + } + function sameInstalledMap(left, right) { + const names = Object.keys(left); + return names.length === Object.keys(right).length && names.every((name) => left[name] === right[name]); + } + /** Sort field choices in the filter panel. */ + const SORT_FIELD_OPTIONS = [ + { + key: "downloads", + label: "sortDownloads" + }, + { + key: "stars", + label: "sortStars" + }, + { + key: "added", + label: "sortAdded" + } + ]; + /** Sort direction choices in the filter panel (labels depend on the field). */ + const SORT_DIR_OPTIONS = ["desc", "asc"]; + /** Published-within choices in the filter panel. */ + const TIME_OPTIONS = [ + { + key: "all", + label: "timeAll" + }, + { + key: "day", + label: "timeDay" + }, + { + key: "week", + label: "timeWeek" + }, + { + key: "month", + label: "timeMonth" + }, + { + key: "quarter", + label: "timeQuarter" + }, + { + key: "year", + label: "timeYear" + } + ]; + function MarketSection(props) { + const t = props.t; + const initialWebdav = (0, react.useMemo)(savedWebdav, []); + const localeSnap = (0, react.useSyncExternalStore)((cb) => props.locale.subscribe(cb), () => props.locale.getSnapshot()); + const lang = String(localeSnap.active).toLowerCase().startsWith("zh") ? "zh" : "en"; + const themeSnap = (0, react.useSyncExternalStore)(props.themeStore.subscribe, props.themeStore.getSnapshot); + const [data, setData] = (0, react.useState)(cachedRegistry); + const [loadError, setLoadError] = (0, react.useState)(null); + const [installed, setInstalledState] = (0, react.useState)(cachedInstalled ?? {}); + const setInstalled = (0, react.useCallback)((value) => { + cachedInstalled = value; + setInstalledState(value); + }, []); + const [repoIdentities, setRepoIdentitiesState] = (0, react.useState)(cachedRepoIdentities ?? {}); + const setRepoIdentities = (0, react.useCallback)((value) => { + cachedRepoIdentities = value; + setRepoIdentitiesState(value); + }, []); + const [repoHints, setRepoHintsState] = (0, react.useState)(cachedRepoHints ?? {}); + const setRepoHints = (0, react.useCallback)((value) => { + cachedRepoHints = value; + setRepoHintsState(value); + }, []); + const [installedFiles, setInstalledFiles] = (0, react.useState)([]); + const [skins, setSkins] = (0, react.useState)([]); + const [tab, setTab] = (0, react.useState)(() => { + const saved = sessionStorage.getItem("dshm-tab"); + if (saved !== null) sessionStorage.removeItem("dshm-tab"); + return saved || "discover"; + }); + const [q, setQ] = (0, react.useState)(""); + /** Per-tab searches stay independent: discover / themes / installed. */ + const [qThemes, setQThemes] = (0, react.useState)(""); + const [qInstalled, setQInstalled] = (0, react.useState)(""); + const [cat, setCat] = (0, react.useState)("all"); + (0, react.useEffect)(() => { + const target = props.preferredSubsectionId; + if (target === void 0) return; + const separator = target.indexOf(":"); + const kind = separator === -1 ? target : target.slice(0, separator); + const value = separator === -1 ? "" : target.slice(separator + 1); + if (kind === "installed") { + setTab("installed"); + setQInstalled(value); + } else if (kind === "discover") { + setTab("discover"); + setCat("all"); + setQ(value); + } + }, [props.preferredSubsectionId]); + const [confirming, setConfirming] = (0, react.useState)(null); + /** The plugin whose comment thread is open, or null. */ + const [commentsFor, setCommentsFor] = (0, react.useState)(null); + /** + * Every mutating operation the user started. Records outlive the card that + * started them, so paginating or searching cannot take a pending decision + * off screen. + */ + const [records, setRecords] = (0, react.useState)([]); + const recordSeq = (0, react.useRef)(0); + /** The synthetic install task rebuilt from dshm-pending after a remount. */ + const recoveredInstall = (0, react.useRef)(null); + /** The synthetic task rebuilt from dshm-updating after this section remounts. */ + const recoveredUpdateRecordId = (0, react.useRef)(null); + /** Raised by the card marker, so "查看详情" lands on the record itself. */ + const [operationsOpen, setOperationsOpen] = (0, react.useState)(false); + const openOperations = (0, react.useCallback)(() => setOperationsOpen(true), []); + /** + * Two plugins can ship under one name from different authors, so a roster + * row that shows only the package name cannot tell the user which of their + * plugins a swap would uninstall. Resolve through the catalog for the + * author and avatar a card would show, and fall back to the bare name for + * anything installed outside it. + */ + const describePlugin = (0, react.useCallback)((name) => { + const entry = data?.plugins.find((plugin) => plugin.npm === name || plugin.name === name); + if (entry === void 0) return { title: name }; + return { + title: pluginName(entry.name), + author: entry.owner === "" ? void 0 : entry.owner, + avatar: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OwnerAvatar, { + name: entry.name, + owner: entry.owner || "" + }) + }; + }, [data]); + /** Ids are sequential rather than random so a replayed session is stable. */ + const nextRecordId = (0, react.useCallback)(() => { + recordSeq.current += 1; + return `op-${String(recordSeq.current)}`; + }, []); + const [replacing, setReplacing] = (0, react.useState)(false); + /** Shared by every screenshot source (card thumbnail, dialog strip). */ + const [lightbox, setLightbox] = (0, react.useState)(null); + const openLightbox = (shots, index) => setLightbox({ + shots, + index + }); + const [themesFullscreen, setThemesFullscreen] = (0, react.useState)(false); + const [busyUrl, setBusyUrl] = (0, react.useState)(null); + /** Consecutive idle polls with a pending install that never landed (#32). */ + const idleStrikes = (0, react.useRef)(0); + /** Same idle-strike bookkeeping for an update whose response was lost. */ + const updateIdleStrikes = (0, react.useRef)(0); + const [doneUrls, setDoneUrls] = (0, react.useState)([]); + const [installError, setInstallError] = (0, react.useState)(null); + const [compatibilityNotice, setCompatibilityNotice] = (0, react.useState)(null); + const [rollingBack, setRollingBack] = (0, react.useState)(false); + /** Log export lifecycle for visible feedback (#84): idle → busy → done/fail. */ + const [exportState, setExportState] = (0, react.useState)("idle"); + /** + * Programmatic log download with explicit feedback (#84) — the plain + * `` gave no sign anything happened, and the error banner's + * "export the log" wording pointed at text that was not clickable at all. + * Success/failure surface as a primitives Toast (body portal, no layout + * impact) instead of inline text. + */ + const doExportLog = (0, react.useCallback)(() => { + setExportState("busy"); + fetch(api("/dsh-market/logs")).then(async (res) => { + if (!res.ok) throw new Error(`HTTP ${String(res.status)}`); + const serverText = await res.text(); + const browser = clientDiagnostics(); + const blob = new Blob([serverText, ...browser.length > 0 ? [ + "## browser\n", + browser.join("\n"), + "\n" + ] : []], { type: "text/plain;charset=utf-8" }); + const url = URL.createObjectURL(blob); + const anchor = document.createElement("a"); + anchor.href = url; + anchor.download = "dsh-market-log.txt"; + document.body.appendChild(anchor); + anchor.click(); + anchor.remove(); + URL.revokeObjectURL(url); + setExportState("done"); + }).catch(() => setExportState("fail")); + }, []); + /** Stable onDone for the export Toast — a fresh closure per render would + * reset the Toast's auto-dismiss timer on every parent re-render. */ + const exportToastDone = (0, react.useCallback)(() => setExportState("idle"), []); + const [updates, setUpdates] = (0, react.useState)({}); + /** Update reminders dismissed for this host boot. The Installed tab still + * shows these plugins and their update actions; only proactive prompts use + * this set. */ + const [ignoredUpdateNames, setIgnoredUpdateNames] = (0, react.useState)([]); + const [updatingName, setUpdatingName] = (0, react.useState)(null); + /** Update-notes dialog (#294): which row opened it, and what it resolved to. */ + const [notesFor, setNotesFor] = (0, react.useState)(null); + const [updateNotes, setUpdateNotes] = (0, react.useState)(null); + const [notesState, setNotesState] = (0, react.useState)("loading"); + const [staleName, setStaleName] = (0, react.useState)(null); + const [restoreName, setRestoreName] = (0, react.useState)(null); + /** Determinate percent parsed from pnpm's Progress line, when available. */ + const [progressPct, setProgressPct] = (0, react.useState)(null); + /** + * Blocked build scripts from the last install or update: enables + * approve-and-retry (#6; updates in #69). Exactly one of `plugin` + * (retry installs it) / `updateName` (retry re-runs the update) is set. + */ + const [buildsSkipped, setBuildsSkipped] = (0, react.useState)(null); + const [updatingAll, setUpdatingAll] = (0, react.useState)(false); + const [updatedNames, setUpdatedNames] = (0, react.useState)([]); + const [hotUrls, setHotUrls] = (0, react.useState)([]); + const [hotNames, setHotNames] = (0, react.useState)([]); + const [progressLine, setProgressLine] = (0, react.useState)(null); + /** Per-package activation states from /dsh-market/installed + operations. */ + const [activations, setActivations] = (0, react.useState)({}); + /** #60: persisted disable list + custom groups, straight from /installed. */ + const [disabledNames, setDisabledNames] = (0, react.useState)([]); + /** The user's own note per plugin (#347): package name → text. */ + const [notes, setNotes] = (0, react.useState)({}); + /** Rows the user asked to show the AUTHOR's description on, despite a note. */ + const [showTheirs, setShowTheirs] = (0, react.useState)([]); + /** The row whose note is being edited, and the text in the box. */ + const [notingName, setNotingName] = (0, react.useState)(null); + const [noteDraft, setNoteDraft] = (0, react.useState)(""); + /** The disable set as of the first load; null until it arrives. */ + const loadedDisabled = (0, react.useRef)(null); + /** + * Patch-layer flags (port of dsh-plugin-hub): packages whose bundle rows + * the user patch layer disables / force-enables. The UI treats them as the + * real switch state so hand-edited cordis.patch.yml toggles are visible. + */ + const [patchDisabledNames, setPatchDisabledNames] = (0, react.useState)([]); + const [groups, setGroups] = (0, react.useState)({}); + const [groupOrder, setGroupOrder] = (0, react.useState)([]); + /** Installed-tab sub-view: flat list or groups (All-plugins was removed — + * it duplicated the Discover tab). */ + const [installedView, setInstalledView] = (0, react.useState)("list"); + const [togglingName, setTogglingName] = (0, react.useState)(null); + const [creatingGroup, setCreatingGroup] = (0, react.useState)(false); + const [newGroupName, setNewGroupName] = (0, react.useState)(""); + const [renamingGroup, setRenamingGroup] = (0, react.useState)(null); + const [renamingValue, setRenamingValue] = (0, react.useState)(""); + const [deletingGroup, setDeletingGroup] = (0, react.useState)(null); + /** Open group picker: which group and whether it adds plugins or themes. */ + const [addPanel, setAddPanel] = (0, react.useState)(null); + const [assignFor, setAssignFor] = (0, react.useState)(null); + const [assignTarget, setAssignTarget] = (0, react.useState)(""); + /** Structured progress from pnpm ndjson (P1-6). */ + const [progressPhase, setProgressPhase] = (0, react.useState)(null); + const [progressCurrent, setProgressCurrent] = (0, react.useState)(null); + const [progressDone, setProgressDone] = (0, react.useState)(0); + const [cancelling, setCancelling] = (0, react.useState)(false); + /** Server-side operation lock from /dsh-market/status (#91). */ + const [hostBusy, setHostBusy] = (0, react.useState)(false); + /** + * The market's own version, shown beside the heading. Most bug reports + * arrive as a photo of the screen, and without a version in frame the + * first reply always has to ask which one it was. + */ + const [version, setVersion] = (0, react.useState)(null); + /** Non-live activation results from the last operation, shown as a banner. */ + const [activationWarnings, setActivationWarnings] = (0, react.useState)([]); + const [hostDependencyFindings, setHostDependencyFindings] = (0, react.useState)([]); + /** Plugin name awaiting uninstall confirmation (Modal). */ + const [removeConfirm, setRemoveConfirm] = (0, react.useState)(null); + const [removingName, setRemovingName] = (0, react.useState)(null); + const [removedCount, setRemovedCount] = (0, react.useState)(0); + /** Toggles whose live fiber did not follow the switch — restart to apply. */ + const [toggleRestart, setToggleRestart] = (0, react.useState)(0); + /** Last completed toggle, shown as a toast (#299). The switch and the row + * tag already say the new state, but both live in a row the user may have + * scrolled past — a mis-click there goes unnoticed. The toast is fixed on + * screen, so it is the part that actually catches an accident. */ + const [toggled, setToggled] = (0, react.useState)(null); + const toggledDone = (0, react.useCallback)(() => setToggled(null), []); + /** + * Dismissal of the host-reported restart notice, keyed to the current boot + * so it reappears after a restart that did not happen and after any new + * change. sessionStorage, not local: closing the tab is a fresh start. + */ + const [restartNoticeDismissed, setRestartNoticeDismissed] = (0, react.useState)(false); + /** Client-part plugins toggled this session — their UI needs a refresh. */ + const [refreshNames, setRefreshNames] = (0, react.useState)([]); + const [envReady, setEnvReady] = (0, react.useState)(true); + const [envFixing, setEnvFixing] = (0, react.useState)(false); + const [envFailed, setEnvFailed] = (0, react.useState)(false); + const [bootId, setBootId] = (0, react.useState)(null); + /** One-click restart (#14 by @ysyyhhh): server capability + in-flight state. */ + const [restartEnabled, setRestartEnabled] = (0, react.useState)(false); + /** Supervisor the host detected around itself, when it named one (#229). */ + const [supervisor, setSupervisor] = (0, react.useState)(null); + /** Debugger latch when one-click restart must not kill the host (#447). */ + const [debuggerLatch, setDebuggerLatch] = (0, react.useState)(null); + const [restarting, setRestarting] = (0, react.useState)(false); + const [showTop, setShowTop] = (0, react.useState)(false); + const [backupBusy, setBackupBusy] = (0, react.useState)(false); + const [backupMessage, setBackupMessage] = (0, react.useState)(null); + const [backupRestored, setBackupRestored] = (0, react.useState)(false); + const [pendingBackup, setPendingBackup] = (0, react.useState)(null); + const [pendingDependencies, setPendingDependencies] = (0, react.useState)({}); + const [webdavUrl, setWebdavUrl] = (0, react.useState)(initialWebdav.url); + const [webdavUser, setWebdavUser] = (0, react.useState)(initialWebdav.username); + const [webdavPassword, setWebdavPassword] = (0, react.useState)(initialWebdav.password); + const [autoBackup, setAutoBackup] = (0, react.useState)(initialWebdav.auto); + /** GitHub token — session memory only, never written to any storage. */ + const [gistToken, setGistToken] = (0, react.useState)(""); + /** Gist id — persisted across reloads (non-sensitive: the Gist itself is private). */ + const [gistId, setGistId] = (0, react.useState)(() => { + try { + return localStorage.getItem("dshm-gist-id") ?? ""; + } catch { + return ""; + } + }); + /** Export mode: 'update' PATCHes the Gist in the field, 'create' makes a new one. */ + const [gistMode, setGistMode] = (0, react.useState)(() => { + try { + return localStorage.getItem("dshm-gist-id") ? "update" : "create"; + } catch { + return "create"; + } + }); + const [gistBusy, setGistBusy] = (0, react.useState)(false); + const [gistMessage, setGistMessage] = (0, react.useState)(null); + const [gistOk, setGistOk] = (0, react.useState)(false); + const [gistResult, setGistResult] = (0, react.useState)(null); + /** Export picker: open state, selected plugin names, include-config flag. */ + const [exportOpen, setExportOpen] = (0, react.useState)(false); + const [exportSelection, setExportSelection] = (0, react.useState)(/* @__PURE__ */ new Set()); + const [exportIncludeConfig, setExportIncludeConfig] = (0, react.useState)(false); + /** Export failure shown INSIDE the picker so it is never hidden behind it. */ + const [exportError, setExportError] = (0, react.useState)(null); + /** Bundle-only plugin names from /dsh-market/installed (picker list). */ + const [installedBundles, setInstalledBundles] = (0, react.useState)([]); + const bodyRef = (0, react.useRef)(null); + /** Hidden file input behind the Import button (a Button can't host an ). */ + const fileInputRef = (0, react.useRef)(null); + const [sortField, setSortField] = (0, react.useState)("downloads"); + const [sortDir, setSortDir] = (0, react.useState)("desc"); + const [timeRange, setTimeRange] = (0, react.useState)("all"); + const [catsOpen, setCatsOpen] = (0, react.useState)(false); + /** Themes tab: independent from Discover's sort/time state above — a + * search or sort choice in one tab has no business resetting the other. */ + const [themeSortField, setThemeSortField] = (0, react.useState)("downloads"); + const [themeSortDir, setThemeSortDir] = (0, react.useState)("desc"); + const [themeTimeRange, setThemeTimeRange] = (0, react.useState)("all"); + /** WebDAV provider-preset dropdown (primitives Menu). */ + const [presetOpen, setPresetOpen] = (0, react.useState)(false); + /** Install-command disclosure inside the confirm dialog. */ + const [cmdOpen, setCmdOpen] = (0, react.useState)(false); + /** Per-row "why is it not live" disclosure (installed tab). */ + const [whyOpen, setWhyOpen] = (0, react.useState)(null); + /** Restore-confirm dialog (replaces window.confirm). */ + const [restoreConfirmOpen, setRestoreConfirmOpen] = (0, react.useState)(false); + /** Plugins that failed to install during a restore (replaces window.alert). */ + const [restoreErrors, setRestoreErrors] = (0, react.useState)([]); + const [visibleCats, setVisibleCats] = (0, react.useState)(null); + /** Same idea as `visibleCats`, but how many fit in a single row — used to + * shrink an expanded (2+ row) category list while the sticky header is + * pinned during scroll, distinct from the two-row collapsed default. */ + const [visibleCatsOneRow, setVisibleCatsOneRow] = (0, react.useState)(null); + const catsWrapRef = (0, react.useRef)(null); + const [catsStuck, setCatsStuck] = (0, react.useState)(false); + const [catsSentinel, setCatsSentinel] = (0, react.useState)(null); + const refreshInstalled = (0, react.useCallback)((force) => { + fetch(api("/dsh-market/installed"), { cache: "no-store" }).then((res) => res.json()).then((body) => { + setInstalled(body.installed || {}); + setRepoIdentities(installedRepoIdentities(body.repoIdentities)); + setRepoHints(installedRepoHints(body.repoHints)); + setInstalledFiles(Array.isArray(body.present) ? body.present : Object.keys(body.installed || {})); + setSkins(body.live || []); + if (Array.isArray(body.disabled)) { + setDisabledNames(body.disabled); + if (loadedDisabled.current === null) loadedDisabled.current = new Set(body.disabled); + } + if (body.notes !== null && typeof body.notes === "object" && !Array.isArray(body.notes)) setNotes(body.notes); + if (Array.isArray(body.patchDisabled)) setPatchDisabledNames(body.patchDisabled); + if (body.groups && typeof body.groups === "object") setGroups(body.groups); + if (Array.isArray(body.groupOrder)) setGroupOrder(body.groupOrder); + setInstalledBundles(Array.isArray(body.bundles) ? body.bundles.filter((name) => typeof name === "string") : []); + if (body.activation && typeof body.activation === "object") setActivations(body.activation); + const findings = body.diagnostics?.schema === "dsh-market/diagnostics/v1" && Array.isArray(body.diagnostics.findings) ? body.diagnostics.findings.filter(isHostDependencyFinding) : []; + setHostDependencyFindings(findings); + }).catch(() => {}); + fetch(api("/dsh-market/updates") + (force === true ? "?force=1" : ""), { cache: "no-store" }).then((res) => res.json()).then((body) => setUpdates(body.updates || {})).catch(() => {}); + }, []); + /** Active Bundles count as installed in Discover without becoming package-manager targets. */ + const catalogInstalled = (0, react.useMemo)(() => installedForCatalog(installed, installedBundles), [installed, installedBundles]); + (0, react.useMemo)(() => new Set(disabledNames), [disabledNames]); + /** Effective switch state: market disable list ∪ user-patch-layer disables. */ + const effectiveDisabledSet = (0, react.useMemo)(() => /* @__PURE__ */ new Set([...disabledNames, ...patchDisabledNames]), [disabledNames, patchDisabledNames]); + (0, react.useEffect)(() => { + if (tab !== "themes" && themesFullscreen) setThemesFullscreen(false); + }, [tab, themesFullscreen]); + (0, react.useEffect)(() => { + if (!themesFullscreen || lightbox !== null) return; + const onKey = (event) => { + if (event.key !== "Escape") return; + event.stopPropagation(); + setThemesFullscreen(false); + }; + window.addEventListener("keydown", onKey, true); + return () => window.removeEventListener("keydown", onKey, true); + }, [lightbox, themesFullscreen]); + const loadCatalog = (0, react.useCallback)(() => { + setLoadError(null); + return fetch(api("/dsh-market/registry"), { cache: "no-store" }).then(async (res) => { + const body = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(typeof body.error === "string" ? body.error : `HTTP ${String(res.status)}`); + return body; + }).then((body) => { + if (body.registry === void 0) throw new Error("the catalog response carried no data"); + cachedRegistry = body.registry; + setData(body.registry); + setLoadError(null); + }).catch((error) => { + setLoadError(error instanceof Error ? error.message : String(error)); + }); + }, []); + (0, react.useEffect)(() => { + loadCatalog(); + fetch(api("/dsh-market/status"), { cache: "no-store" }).then((res) => res.json()).then((status) => { + setEnvReady(status.pnpm !== false); + setGithubProxy(typeof status.githubProxy === "string" ? status.githubProxy : null); + if (typeof status.boot === "string") { + setBootId(status.boot); + setIgnoredUpdateNames(ignoredUpdatesForBoot(status.boot)); + try { + setRestartNoticeDismissed(sessionStorage.getItem("dshm-restart-dismissed") === status.boot); + } catch {} + } + setRestartEnabled(status.restart === true); + setSupervisor(typeof status.supervisor === "string" ? status.supervisor : null); + setDebuggerLatch(typeof status.debugger === "string" ? status.debugger : null); + if (typeof status.version === "string" && status.version !== "") setVersion(status.version); + }).catch(() => {}); + refreshInstalled(); + }, [refreshInstalled, loadCatalog]); + (0, react.useEffect)(() => { + if (bootId === null) return; + const saved = readSession("dshm-restart"); + if (saved === null) return; + if (saved.boot !== bootId) { + sessionStorage.removeItem("dshm-restart"); + return; + } + if (Array.isArray(saved.doneUrls) && saved.doneUrls.length > 0) setDoneUrls(saved.doneUrls); + if (Array.isArray(saved.updated) && saved.updated.length > 0) setUpdatedNames(saved.updated); + if (typeof saved.removed === "number" && saved.removed > 0) setRemovedCount(saved.removed); + if (typeof saved.toggled === "number" && saved.toggled > 0) setToggleRestart(saved.toggled); + }, [bootId]); + (0, react.useEffect)(() => { + if (bootId === null) return; + if (doneUrls.length === 0 && updatedNames.length === 0 && removedCount === 0 && toggleRestart === 0) { + sessionStorage.removeItem("dshm-restart"); + return; + } + sessionStorage.setItem("dshm-restart", JSON.stringify({ + boot: bootId, + doneUrls, + updated: updatedNames, + removed: removedCount, + toggled: toggleRestart + })); + }, [ + bootId, + doneUrls, + updatedNames, + removedCount, + toggleRestart + ]); + const fixEnv = (0, react.useCallback)(() => { + setEnvFixing(true); + setEnvFailed(false); + fetch(api("/dsh-market/setup-pnpm"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: "{}" + }).then((res) => res.json()).then((body) => { + if (body.ok) setEnvReady(true); + else { + setEnvFailed(true); + if (typeof body.error === "string") setInstallError(body.error); + } + }).catch(() => setEnvFailed(true)).finally(() => setEnvFixing(false)); + }, []); + (0, react.useEffect)(() => { + const pending = readSession("dshm-pending"); + if (pending !== null && typeof pending.url === "string") { + setBusyUrl(pending.url); + recoveredInstall.current = { + id: `recovered-install:${pending.url}`, + url: pending.url, + ...typeof pending.name === "string" && pending.name !== "" ? { name: pending.name } : {} + }; + } + const updating = readSession("dshm-updating"); + if (updating !== null && typeof updating.name === "string" && updating.name !== "") { + setUpdatingName(updating.name); + const id = `recovered-update:${updating.name}`; + recoveredUpdateRecordId.current = id; + setRecords((list) => list.some((record) => record.kind === "update" && record.name === updating.name && record.state === "running") ? list : enqueue(list, { + id, + kind: "update", + name: updating.name, + state: "running" + })); + } + }, []); + (0, react.useEffect)(() => { + const recovered = recoveredInstall.current; + if (recovered === null) return; + const name = recovered.name ?? data?.plugins.find((plugin) => plugin.url === recovered.url)?.name; + if (name === void 0) return; + recovered.name = name; + setRecords((list) => list.some((record) => record.id === recovered.id) ? list : enqueue(list, { + id: recovered.id, + kind: "install", + name, + url: recovered.url, + state: "running" + })); + }, [data]); + (0, react.useEffect)(() => { + if (busyUrl === null && updatingName === null) { + setHostBusy(false); + setProgressLine(null); + setProgressPhase(null); + setProgressCurrent(null); + setProgressDone(0); + setCancelling(false); + return; + } + const timer = setInterval(() => { + fetch(api("/dsh-market/status"), { cache: "no-store" }).then((res) => res.json()).then((status) => { + setHostBusy(status.busy === true); + setDebuggerLatch(typeof status.debugger === "string" ? status.debugger : null); + if (status.active) { + setCancelling(status.cancelling === true); + if (status.phase !== null && status.phase !== void 0) { + setProgressPhase(status.phase); + setProgressCurrent(status.currentPackage ?? null); + setProgressDone(status.done ?? 0); + setProgressLine(null); + if (typeof status.size === "number" && status.size > 0 && typeof status.downloaded === "number") setProgressPct(Math.max(4, Math.min(96, Math.round(status.downloaded / status.size * 100)))); + } else { + setProgressLine((status.lastLine || "…") + " (" + status.seconds + "s)"); + setProgressPhase(null); + setProgressCurrent(null); + setProgressDone(0); + const m = /resolved (\d+), reused (\d+), downloaded (\d+), added (\d+)/.exec(status.lastLine || ""); + if (m !== null && Number(m[1]) > 0) { + const done = Number(m[2]) + Number(m[3]) + Number(m[4]); + setProgressPct(Math.max(4, Math.min(96, Math.round(done / Number(m[1]) * 100)))); + } + } + } else { + setProgressLine(null); + setProgressPct(null); + setProgressPhase(null); + setProgressCurrent(null); + setProgressDone(0); + setCancelling(false); + const statusInstalled = installedMap(status.installed); + if (!sameInstalledMap(installed, statusInstalled)) refreshInstalled(); + if (readSession("dshm-pending") !== null && busyUrl !== null && status.busy !== true) { + if (data !== null && data.plugins.some((p) => p.url === busyUrl && isInstalled(p, statusInstalled, repoIdentities, data.plugins, repoHints))) { + idleStrikes.current = 0; + sessionStorage.removeItem("dshm-pending"); + const recovered = recoveredInstall.current; + if (recovered !== null) { + setRecords((list) => drop(list, recovered.id)); + recoveredInstall.current = null; + } + setDoneUrls((urls) => urls.includes(busyUrl) ? urls : urls.concat(busyUrl)); + setBusyUrl(null); + } else if (++idleStrikes.current >= 2) { + idleStrikes.current = 0; + sessionStorage.removeItem("dshm-pending"); + const recovered = recoveredInstall.current; + if (recovered !== null) { + setRecords((list) => drop(list, recovered.id)); + recoveredInstall.current = null; + } + setBusyUrl(null); + setInstallError(t("installFail") + " — " + t("exportLog")); + } + } + if (updatingName !== null && status.busy !== true) { + if (++updateIdleStrikes.current >= 2) { + updateIdleStrikes.current = 0; + sessionStorage.removeItem("dshm-updating"); + const recoveredId = recoveredUpdateRecordId.current; + if (recoveredId !== null) { + setRecords((list) => drop(list, recoveredId)); + recoveredUpdateRecordId.current = null; + } + setUpdatingName(null); + refreshInstalled(); + } + } else updateIdleStrikes.current = 0; + } + }).catch(() => {}); + }, 2e3); + return () => clearInterval(timer); + }, [ + busyUrl, + updatingName, + data, + installed, + repoIdentities, + repoHints, + refreshInstalled + ]); + const scrollToTop = () => { + const el = bodyRef.current; + if (el) { + if (typeof el.scrollTo === "function") el.scrollTo({ + top: 0, + behavior: "smooth" + }); + else el.scrollTop = 0; + } + }; + (0, react.useLayoutEffect)(() => { + const el = bodyRef.current; + if (el !== null) el.scrollTop = 0; + setShowTop(false); + }, [ + tab, + q, + cat, + sortField, + sortDir, + timeRange, + qThemes, + themeSortField, + themeSortDir, + themeTimeRange, + qInstalled, + installedView + ]); + const plugins = (0, react.useMemo)(() => data === null ? [] : visiblePlugins(data.plugins, { + category: cat, + query: q, + lang, + categories: data.categories, + sort: `${sortField}-${sortDir}`, + sinceDays: timeRange === "all" ? void 0 : TIME_RANGE_DAYS[timeRange] + }), [ + data, + q, + cat, + lang, + sortField, + sortDir, + timeRange + ]); + const { currentPage, totalPages, pageSize, goToPage, changePageSize } = usePagination(plugins.length, [ + q, + cat, + sortField, + sortDir, + timeRange + ], scrollToTop); + const pagePlugins = plugins.slice((currentPage - 1) * pageSize, currentPage * pageSize); + const themePlugins$1 = (0, react.useMemo)(() => data === null ? [] : visiblePlugins(data.plugins, { + category: "theme", + query: qThemes, + lang, + categories: data.categories, + sort: `${themeSortField}-${themeSortDir}`, + sinceDays: themeTimeRange === "all" ? void 0 : TIME_RANGE_DAYS[themeTimeRange] + }), [ + data, + qThemes, + lang, + themeSortField, + themeSortDir, + themeTimeRange + ]); + const themePagination = usePagination(themePlugins$1.length, [ + qThemes, + themeSortField, + themeSortDir, + themeTimeRange + ], scrollToTop); + const themePagePlugins = themePlugins$1.slice((themePagination.currentPage - 1) * themePagination.pageSize, themePagination.currentPage * themePagination.pageSize); + /** Download a host endpoint as a file — primitives Button can't be an . + * Prefers the server's Content-Disposition filename (e.g. the timestamped + * backup export) and falls back to the caller's name. */ + const downloadFile = (0, react.useCallback)((url, filename) => { + fetch(url).then((res) => { + if (!res.ok) throw new Error("HTTP " + res.status); + const disposition = res.headers.get("content-disposition"); + if (disposition !== null) { + const match = /filename="?([^";]+)"?/.exec(disposition); + if (match !== null && match[1] !== void 0 && match[1] !== "") filename = match[1]; + } + return res.blob(); + }).then((blob) => { + const a = document.createElement("a"); + a.href = URL.createObjectURL(blob); + a.download = filename; + a.click(); + setTimeout(() => URL.revokeObjectURL(a.href), 2e3); + }).catch((error) => setInstallError(String(error))); + }, []); + const doRollback = (0, react.useCallback)((rollbackId) => { + setRollingBack(true); + setInstallError(null); + fetch(api("/dsh-market/rollback"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ rollbackId }) + }).then((res) => res.json().then((body) => ({ + status: res.status, + body + }))).then(({ status, body }) => { + if (status === 200 && body.ok) { + setCompatibilityNotice(null); + refreshInstalled(); + } else setInstallError(String(body.error || body.detail || "rollback failed")); + }).catch((error) => setInstallError(String(error))).finally(() => setRollingBack(false)); + }, [refreshInstalled]); + const compatibilitySummary = (risks) => { + if (risks.length === 0) return ""; + const first = risks[0]; + return `${first.plugin}: ${first.peer} ${first.range} vs ${first.resolved}`; + }; + /** Which name now resolves from two layers, and which layers those are. */ + const shadowSummary = (entries) => { + if (entries.length === 0) return ""; + const first = entries[0]; + const rest = entries.length > 1 ? ` (+${entries.length - 1})` : ""; + return `${first.name} — ${first.layers.join(" / ")}${rest}`; + }; + const doInstall = (0, react.useCallback)((plugin) => { + setBuildsSkipped(null); + setConfirming(null); + setInstallError(null); + setActivationWarnings([]); + setBusyUrl(plugin.url); + const recordId = nextRecordId(); + setRecords((list) => enqueue(list, { + id: recordId, + kind: "install", + name: plugin.name, + url: plugin.url, + state: "running" + })); + sessionStorage.setItem("dshm-pending", JSON.stringify({ + url: plugin.url, + name: plugin.name + })); + fetch(api("/dsh-market/install"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ url: plugin.url }) + }).then((res) => res.json().then((body) => ({ + status: res.status, + body + }))).then(({ status, body }) => { + setBusyUrl(null); + sessionStorage.removeItem("dshm-pending"); + if (status === 200 && body.ok && body.hot && pluginCategories(plugin).includes("theme")) { + sessionStorage.setItem("dshm-toast", JSON.stringify([plugin.name])); + sessionStorage.setItem("dshm-tab", "themes"); + location.reload(); + return; + } + if (body.cancelled === true) { + setRecords((list) => drop(list, recordId)); + refreshInstalled(); + if (body.partial === true) setInstallError(t("partialNote")); + return; + } + if (status === 200 && body.ok) { + sessionStorage.setItem("dshm-tab", "installed"); + if (body.activation && typeof body.activation === "object") { + setActivations((prev) => ({ + ...prev, + ...body.activation + })); + const warns = Object.entries(body.activation).filter(([, info]) => info.state !== "live" && info.state !== "missing").map(([name, info]) => ({ + name, + info + })); + setActivationWarnings(warns); + } + if (body.hot) { + setDoneUrls((urls) => urls.filter((url) => url !== plugin.url)); + setHotUrls((urls) => urls.includes(plugin.url) ? urls : urls.concat(plugin.url)); + setHotNames((names) => names.includes(plugin.name) ? names : names.concat(plugin.name)); + } else setDoneUrls((urls) => urls.includes(plugin.url) ? urls : urls.concat(plugin.url)); + if (body.compatibility?.code === "soft-incompatible") setCompatibilityNotice(body.compatibility); + setRecords((list) => patch(list, recordId, body.compatibility?.code === "soft-incompatible" ? { + state: "warned", + reason: t("compatRiskBanner") + } : { + state: "done", + needsRefresh: body.hot !== true + })); + refreshInstalled(); + } else { + if (status === 409) { + const busyReason = body.agentsBusy === true ? t("agentBusyInstall") + (Array.isArray(body.runningAgents) && body.runningAgents.length > 0 ? ` (${body.runningAgents.join(", ")})` : "") : t("busyWait"); + setRecords((list) => patch(list, recordId, { + state: "failed", + reason: busyReason + })); + setOperationsOpen(true); + return; + } + if (Array.isArray(body.conflictGroups) && body.conflictGroups.length > 0) { + setRecords((list) => patch(list, recordId, { + state: "input", + conflicts: body.conflictGroups + })); + setOperationsOpen(true); + return; + } + const blocked = Array.isArray(body.ignoredBuilds) ? body.ignoredBuilds.map(String) : []; + if (blocked.length > 0) setBuildsSkipped({ + plugin, + names: blocked + }); + const text = (v) => typeof v === "string" ? v : v && typeof v.text === "string" ? v.text : v == null ? "" : JSON.stringify(v); + const orphans = Array.isArray(body.orphanBundles) ? body.orphanBundles.map(String) : []; + const failure = text(body.error) || humanOutput([text(body.stderr), text(body.stdout)].filter(Boolean).join("\n")) || "exit " + body.exitCode; + const staleEntry = typeof body.staleEntry === "string" ? body.staleEntry : null; + const detail = [ + orphans.length > 0 ? `${t("orphanBundle")} ${orphans.join(", ")}` : null, + staleEntry, + failure + ].filter(Boolean).join("\n"); + setRecords((list) => patch(list, recordId, { + state: "failed", + reason: detail.trim().slice(-600), + ...blocked.length > 0 ? { blockedBuilds: blocked } : {} + })); + setOperationsOpen(true); + } + }).catch(() => {}); + }, [ + nextRecordId, + refreshInstalled, + t + ]); + /** + * Resolve a loader-id clash the only way one profile allows: uninstall the + * plugins holding the ids, then retry the install. Sequential because each + * route takes the host's mutation lock, so a parallel burst would 409. + * + * A failure part-way leaves plugins already gone. Nothing reinstalls them + * automatically (a rollback would itself be an install that can fail), so + * the message names them — reporting only "failed" would leave the user + * guessing which of their plugins survived. + */ + const doReplace = (0, react.useCallback)(async (record, plugin) => { + setInstallError(null); + setReplacing(true); + const removed = []; + try { + for (const group of record.conflicts ?? []) { + const response = await fetch(api("/dsh-market/uninstall"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ name: group.owner }) + }); + const body = await response.json(); + if (response.status !== 200 || body.ok !== true) { + const text = (v) => typeof v === "string" ? v : v == null ? "" : JSON.stringify(v); + const detail = (text(body.error) || humanOutput(text(body.stderr)) || "error").trim().slice(-400); + const reason = removed.length === 0 ? `${t("installFail")}: ${group.owner} — ${detail}` : `${t("conflictReplaceFailed")} ${removed.join(", ")} — ${detail}`; + setRecords((list) => patch(list, record.id, { + state: "failed", + conflicts: void 0, + reason + })); + setOperationsOpen(true); + refreshInstalled(); + return; + } + removed.push(group.owner); + } + } finally { + setReplacing(false); + } + setRecords((list) => drop(list, record.id)); + refreshInstalled(); + doInstall(plugin); + }, [ + doInstall, + refreshInstalled, + t + ]); + /** + * Answer a clash. `keep` is not a no-op to skip: it is the user declining + * the install, so the record retires rather than lingering as unanswered. + */ + const resolveConflict = (0, react.useCallback)((record, choice) => { + if (choice === "keep") { + setRecords((list) => patch(list, record.id, { + state: "failed", + conflicts: void 0, + reason: t("conflictDeclined") + })); + return; + } + const plugin = data?.plugins.find((candidate) => candidate.url === record.url); + if (plugin === void 0) return; + doReplace(record, plugin); + }, [ + data, + doReplace, + t + ]); + /** + * Restart the host and reload once the boot id changes (#14 by @ysyyhhh). + * The 202 races the process's SIGTERM, so network errors on the initial + * request are expected and treated as "restart under way". + */ + const doRestart = (0, react.useCallback)(() => { + if (bootId === null || restarting) return; + const previousBoot = bootId; + setRestarting(true); + setInstallError(null); + const awaitNewBoot = () => { + const deadline = Date.now() + 6e4; + const poll = () => { + fetch(api("/dsh-market/status"), { cache: "no-store" }).then((res) => res.json()).then((next) => { + if (typeof next.boot === "string" && next.boot !== previousBoot) { + location.reload(); + return; + } + retry(); + }).catch(retry); + }; + const retry = () => { + if (Date.now() > deadline) { + setRestarting(false); + setInstallError(t("restartTimeout")); + return; + } + setTimeout(poll, 1500); + }; + poll(); + }; + const requestRestart = (attemptsLeft) => { + fetch(api("/dsh-market/restart"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: "{}" + }).then((res) => res.json().then((body) => ({ + status: res.status, + body + }))).then(({ status, body }) => { + if (status === 202 && body.ok === true) { + awaitNewBoot(); + return; + } + if (status === 409 && attemptsLeft > 0) { + setTimeout(() => requestRestart(attemptsLeft - 1), 1500); + return; + } + setRestarting(false); + setInstallError(t("restartFail") + ": " + String(body.error || "HTTP " + String(status))); + }).catch(awaitNewBoot); + }; + requestRestart(10); + }, [ + bootId, + restarting, + t + ]); + /** Cancel the running plugin command (#6 by @qichuang321). */ + const doCancel = (0, react.useCallback)(() => { + fetch(api("/dsh-market/cancel"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: "{}" + }).catch(() => {}); + }, []); + const doUpdate = (0, react.useCallback)((name, force = false, restore = false) => { + setInstallError(null); + setActivationWarnings([]); + setStaleName((prev) => prev === name ? null : prev); + setRestoreName((prev) => prev === name ? null : prev); + setUpdatingName(name); + updateIdleStrikes.current = 0; + sessionStorage.setItem("dshm-updating", JSON.stringify({ name })); + const updateRecordId = nextRecordId(); + setRecords((list) => enqueue(list, { + id: updateRecordId, + kind: "update", + name, + state: "running" + })); + return fetch(api("/dsh-market/update"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + name, + ...force ? { force: true } : {}, + ...restore ? { restore: true } : {} + }) + }).then((res) => res.json().then((body) => ({ + status: res.status, + body + }))).then(({ status, body }) => { + sessionStorage.removeItem("dshm-updating"); + setUpdatingName(null); + if (body.cancelled === true) { + setRecords((list) => drop(list, updateRecordId)); + refreshInstalled(); + if (body.partial === true) setInstallError(t("partialNote")); + return; + } + if (status === 200 && body.ok) { + setRecords((list) => patch(list, updateRecordId, { state: "done" })); + setUpdatedNames((names) => names.concat(name)); + if (body.activation && typeof body.activation === "object") setActivations((prev) => ({ + ...prev, + ...body.activation + })); + if (body.compatibility?.code === "soft-incompatible") setCompatibilityNotice(body.compatibility); + refreshInstalled(); + } else { + if (status === 409) { + if (body.agentsBusy === true) { + const running = Array.isArray(body.runningAgents) && body.runningAgents.length > 0 ? ` (${body.runningAgents.join(", ")})` : ""; + setRecords((list) => patch(list, updateRecordId, { + state: "failed", + reason: t("agentBusyUpdate") + running + })); + setInstallError(t("agentBusyUpdate") + running); + return; + } + setRecords((list) => patch(list, updateRecordId, { + state: "failed", + reason: t("busyWait") + })); + setInstallError(t("busyWait")); + return; + } + if (body.stale === true) setStaleName(name); + if (Array.isArray(body.ignoredBuilds) && body.ignoredBuilds.length > 0) setBuildsSkipped({ + updateName: name, + names: body.ignoredBuilds.map(String), + restore + }); + const text = (v) => typeof v === "string" ? v : v && typeof v.text === "string" ? v.text : v == null ? "" : JSON.stringify(v); + const orphans = Array.isArray(body.orphanBundles) ? body.orphanBundles.map(String) : []; + const failure = text(body.error) || humanOutput([text(body.stderr), text(body.stdout)].filter(Boolean).join("\n")) || "exit " + body.exitCode; + const staleEntry = typeof body.staleEntry === "string" ? body.staleEntry : null; + const detail = [ + orphans.length > 0 ? `${t("orphanBundle")} ${orphans.join(", ")}` : null, + staleEntry, + failure + ].filter(Boolean).join("\n"); + setRecords((list) => patch(list, updateRecordId, { + state: "failed", + reason: detail.trim().slice(-600) + })); + setInstallError((restore ? t("restoreFail") : t("updateFail")) + ": " + name + " — " + detail.trim().slice(-600)); + } + }).catch(() => {}); + }, [refreshInstalled, t]); + const askRestore = (0, react.useCallback)((name) => { + const spec = installed[name]; + const entry = data === null || spec === void 0 ? void 0 : entryForDep(data.plugins, name, String(spec), repoIdentities[name], repoHints[name]); + setStaleName(null); + if (entry === void 0) { + setRestoreName(null); + setInstallError(t("restoreNoCatalog")); + return; + } + setRestoreName(name); + setInstallError(t("restoreHint")); + }, [ + data, + installed, + repoHints, + repoIdentities, + t + ]); + /** Open the update-notes dialog and start its fetch. Lazy: the request only + exists while a user is actually looking at one plugin's notes, and + closing the dialog abandons the render — the server side caches the + payload, so reopening is cheap. */ + const openNotes = (0, react.useCallback)((name, current, latest, repoUrl) => { + setNotesFor({ + name, + current, + latest, + repoUrl + }); + setUpdateNotes(null); + setNotesState("loading"); + fetch(`${api("/dsh-market/changelog")}?name=${encodeURIComponent(name)}`).then((res) => res.json()).then((body) => { + setUpdateNotes(body); + setNotesState("ready"); + }).catch(() => setNotesState("fail")); + }, []); + const doUseSkin = (0, react.useCallback)((name) => { + setInstallError(null); + fetch(api("/dsh-market/use-skin"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ name }) + }).then((res) => res.json().then((body) => ({ + status: res.status, + body + }))).then(({ status, body }) => { + if (status === 200 && body.ok) { + sessionStorage.setItem("dshm-toast", JSON.stringify([name])); + sessionStorage.setItem("dshm-toast-mode", "theme"); + sessionStorage.setItem("dshm-tab", "themes"); + location.reload(); + } else setInstallError(String(body.error || "failed")); + }).catch((error) => setInstallError(String(error))); + }, []); + /** + * Forget a pending page-refresh for a plugin that is no longer here. + * + * The banner counts what the page has not caught up with. Install then + * uninstall and the page is level again — there is nothing left to load — + * but both sets were append-only, so it kept asking for a refresh that + * would show nothing (#340). It conflated "something needs doing" with + * "something happened in this session". + */ + /** Write (or clear, when empty) this plugin's note. */ + const saveNote = (0, react.useCallback)((name, text) => { + setNotingName(null); + fetch(api("/dsh-market/note"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + name, + text + }) + }).then((res) => res.json()).then((body) => { + if (body.ok && body.notes !== null && typeof body.notes === "object") setNotes(body.notes); + else setInstallError(String(body.error || "note failed")); + }).catch((error) => setInstallError(String(error))); + }, []); + const clearPendingRefresh = (0, react.useCallback)((name) => { + setHotNames((names) => names.filter((entry) => entry !== name)); + setRefreshNames((names) => names.filter((entry) => entry !== name)); + }, []); + const doUninstall = (0, react.useCallback)((name) => { + setRemoveConfirm(null); + setInstallError(null); + setActivationWarnings([]); + setRemovingName(name); + return fetch(api("/dsh-market/uninstall"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ name }) + }).then((res) => res.json().then((body) => ({ + status: res.status, + body + }))).then(({ status, body }) => { + if (status === 200 && body.ok) { + if (!body.hot) setRemovedCount((n) => n + 1); + const neverLoadedHere = hotNames.includes(name) || refreshNames.includes(name); + if (body.refresh === true && !neverLoadedHere) setRefreshNames((names) => names.includes(name) ? names : names.concat(name)); + else clearPendingRefresh(name); + refreshInstalled(); + } else { + if (body.cancelled === true) { + refreshInstalled(); + if (body.partial === true) setInstallError(t("partialNote")); + return; + } + if (body.reconciled === true) { + if (!body.hot) setRemovedCount((n) => n + 1); + clearPendingRefresh(name); + refreshInstalled(); + setInstallError(t("reconciledNote")); + return; + } + const text = (v) => typeof v === "string" ? v : v && typeof v.text === "string" ? v.text : v == null ? "" : JSON.stringify(v); + setInstallError((text(body.error) || humanOutput(text(body.stderr)) || "error").trim().slice(-600)); + } + }).catch((error) => setInstallError(String(error))).finally(() => setRemovingName(null)); + }, [ + refreshInstalled, + hotNames, + refreshNames + ]); + /** Live enable/disable of one installed plugin (#60). `reload` opts the + * card-level theme flow into a page refresh so the visual result lands + * immediately (mirrors the use-skin reload on activate). */ + const doToggle = (0, react.useCallback)((name, enabled, reload = false) => { + setTogglingName(name); + setInstallError(null); + return fetch(api("/dsh-market/toggle"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + name, + enabled + }) + }).then((res) => res.json().then((body) => ({ + status: res.status, + body + }))).then(({ status, body }) => { + if (status === 200 && body.ok) { + if (Array.isArray(body.disabled)) setDisabledNames(body.disabled); + if (Array.isArray(body.live)) setSkins(body.live); + if (body.activation && typeof body.activation === "object") setActivations((prev) => ({ + ...prev, + ...body.activation + })); + if (body.restart === true) setToggleRestart((n) => n + 1); + if (body.refresh === true) { + if ((loadedDisabled.current?.has(name) ?? false) === !enabled) clearPendingRefresh(name); + else setRefreshNames((names) => names.includes(name) ? names : names.concat(name)); + } + if (!reload) setToggled({ + name, + enabled + }); + refreshInstalled(); + if (reload) { + sessionStorage.removeItem("dshm-toast"); + sessionStorage.removeItem("dshm-toast-mode"); + sessionStorage.setItem("dshm-tab", "themes"); + location.reload(); + } + } else { + const text = (v) => typeof v === "string" ? v : v == null ? "" : JSON.stringify(v); + setInstallError(text(body.reason) || text(body.error) || t("toggleFail")); + if (body.restart === true) setToggleRestart((n) => n + 1); + if (body.refresh === true) setRefreshNames((names) => names.includes(name) ? names : names.concat(name)); + } + }).catch((error) => setInstallError(String(error))).finally(() => setTogglingName(null)); + }, [ + clearPendingRefresh, + refreshInstalled, + t + ]); + /** Adopt the groups payload returned by POST /dsh-market/groups. */ + const setGroupPayload = (0, react.useCallback)((body) => { + if (body.groups && typeof body.groups === "object") setGroups(body.groups); + if (Array.isArray(body.groupOrder)) setGroupOrder(body.groupOrder); + if (Array.isArray(body.disabled)) setDisabledNames(body.disabled); + }, []); + /** One POST /dsh-market/groups round trip (create/rename/delete/members/toggle). */ + const doGroupAction = (0, react.useCallback)((payload) => { + setInstallError(null); + return fetch(api("/dsh-market/groups"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(payload) + }).then((res) => res.json().then((body) => ({ + status: res.status, + body + }))).then(({ status, body }) => { + if (status === 200 && body.ok) { + setGroupPayload(body); + if (Array.isArray(body.restartMembers) && body.restartMembers.length > 0) setToggleRestart((n) => n + body.restartMembers.length); + if (Array.isArray(body.refreshMembers) && body.refreshMembers.length > 0) setRefreshNames((names) => [.../* @__PURE__ */ new Set([...names, ...body.refreshMembers])]); + refreshInstalled(); + return true; + } + const text = (v) => typeof v === "string" ? v : v == null ? "" : JSON.stringify(v); + setInstallError(text(body.error) || t("toggleFail")); + if (Array.isArray(body.restartMembers) && body.restartMembers.length > 0) setToggleRestart((n) => n + body.restartMembers.length); + if (Array.isArray(body.refreshMembers) && body.refreshMembers.length > 0) setRefreshNames((names) => [.../* @__PURE__ */ new Set([...names, ...body.refreshMembers])]); + return false; + }).catch((error) => { + setInstallError(String(error)); + return false; + }); + }, [ + refreshInstalled, + setGroupPayload, + t + ]); + /** Approve the build scripts pnpm refused, then rerun what was blocked. */ + const approveAndRetry = (0, react.useCallback)((names, resume) => { + fetch(api("/dsh-market/approve-builds"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ packages: names }) + }).then((res) => res.json()).then((body) => { + if (!body.ok) setInstallError(String(body.error || "approve failed")); + else resume(); + }).catch((error) => setInstallError(String(error))); + }, []); + const doGroupToggle = (0, react.useCallback)((name, enabled) => { + return doGroupAction({ + action: "toggle", + name, + enabled + }); + }, [doGroupAction]); + const doCreateGroup = (0, react.useCallback)(() => { + const name = newGroupName.trim(); + if (name === "") return; + doGroupAction({ + action: "create", + name + }).then((ok) => { + if (ok) { + setCreatingGroup(false); + setNewGroupName(""); + } + }); + }, [doGroupAction, newGroupName]); + const doRenameGroup = (0, react.useCallback)((name) => { + const newName = renamingValue.trim(); + if (newName === "" || newName === name) { + setRenamingGroup(null); + return; + } + doGroupAction({ + action: "rename", + name, + newName + }).then((ok) => { + if (ok) { + setRenamingGroup(null); + setRenamingValue(""); + } + }); + }, [doGroupAction, renamingValue]); + const doDeleteGroup = (0, react.useCallback)((name) => { + doGroupAction({ + action: "delete", + name + }).then((ok) => { + if (ok) setDeletingGroup(null); + }); + }, [doGroupAction]); + const doAssign = (0, react.useCallback)((name) => { + const group = assignTarget; + if (group === "") return; + const members = groups[group] ?? []; + doGroupAction({ + action: "set-members", + name: group, + members: [...members, name] + }).then((ok) => { + if (ok) { + setAssignFor(null); + setAssignTarget(""); + } + }); + }, [ + assignTarget, + doGroupAction, + groups + ]); + const doRemoveMember = (0, react.useCallback)((group, name) => { + const members = (groups[group] ?? []).filter((member) => member !== name); + doGroupAction({ + action: "set-members", + name: group, + members + }); + }, [doGroupAction, groups]); + /** Add one installed plugin to a group (picker stays open for batch adds). */ + const doAddMember = (0, react.useCallback)((group, name) => { + const members = groups[group] ?? []; + doGroupAction({ + action: "set-members", + name: group, + members: [...members, name] + }); + }, [doGroupAction, groups]); + const selfName = installed["dshmarket"] !== void 0 ? "dshmarket" : "dsh-market"; + const updatableNames = Object.keys(installed).filter((name) => name !== selfName && !updatedNames.includes(name) && updates[name] && updates[name].updateAvailable); + const batchUpdatableNames = updatableNames.filter((name) => updates[name]?.restoreRequired !== true); + const ignoredUpdateSet = (0, react.useMemo)(() => new Set(ignoredUpdateNames), [ignoredUpdateNames]); + const reminderUpdatableNames = updatableNames.filter((name) => !ignoredUpdateSet.has(name)); + const reminderBatchUpdatableNames = batchUpdatableNames.filter((name) => !ignoredUpdateSet.has(name)); + const reminderUpdateNames = [...updates[selfName]?.updateAvailable === true && !updatedNames.includes(selfName) ? [selfName] : [], ...updatableNames].filter((name) => !ignoredUpdateSet.has(name)); + const installedOtherCount = Object.keys(installed).filter((name) => name !== selfName).length; + const ignoreUpdateNotices = (0, react.useCallback)((names) => { + if (bootId === null || names.length === 0) return; + setIgnoredUpdateNames((current) => { + const next = [.../* @__PURE__ */ new Set([...current, ...names])]; + try { + sessionStorage.setItem(IGNORED_UPDATES_SESSION_KEY, JSON.stringify({ + boot: bootId, + names: next + })); + } catch {} + return next; + }); + }, [bootId]); + const doUpdateAll = (0, react.useCallback)(() => { + const names = reminderBatchUpdatableNames.slice(); + setUpdatingAll(true); + const next = () => { + const name = names.shift(); + if (name === void 0) { + setUpdatingAll(false); + return; + } + doUpdate(name).then(next, next); + }; + next(); + }, [reminderBatchUpdatableNames, doUpdate]); + const finishRestore = (0, react.useCallback)((body) => { + const errors = Array.isArray(body.errors) ? body.errors : []; + const unportable = Array.isArray(body.unportable) ? body.unportable : []; + const bootErrors = Array.isArray(body.bootErrors) ? body.bootErrors.map(String) : []; + setRestoreErrors([ + ...errors.map((item) => `${String(item.name)}: ${String(item.error)}`), + ...unportable.map((item) => `${String(item.name)}: ${t("restoreUnportable")} (${String(item.spec)})`), + ...bootErrors.map((line) => `${t("restoreBootError")} ${line}`) + ]); + setBackupRestored(true); + setBackupMessage(t("restoreDone")); + if (errors.length === 0) { + setPendingBackup(null); + setPendingDependencies({}); + } + refreshInstalled(true); + }, [refreshInstalled, t]); + const previewBackup = (0, react.useCallback)((backup) => { + const dependencies = backupDependencies(backup); + setPendingBackup(backup); + setPendingDependencies(dependencies); + setBackupMessage(t("restorePreviewDone")); + setRestoreErrors([]); + setTab("installed"); + }, [t]); + /** Actually run the restore; the confirm dialog gates this (previously window.confirm). */ + const doRestore = (0, react.useCallback)(() => { + if (pendingBackup === null) return Promise.resolve(); + setRestoreConfirmOpen(false); + setBackupBusy(true); + setBackupMessage(null); + setRestoreErrors([]); + return fetch(api("/dsh-market/restore"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ backup: pendingBackup }) + }).then(async (response) => { + const body = await response.json(); + if (!response.ok) throw new Error(String(body.error || "restore failed")); + finishRestore(body); + }).catch((error) => setBackupMessage(String(error))).finally(() => setBackupBusy(false)); + }, [finishRestore, pendingBackup]); + const runWebdav = (0, react.useCallback)((action) => { + if (webdavUrl.trim() === "") return; + setBackupBusy(true); + setBackupMessage(null); + setRestoreErrors([]); + fetch(api("/dsh-market/webdav"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + action, + url: webdavUrl.trim(), + username: webdavUser, + password: webdavPassword + }) + }).then(async (response) => { + const body = await response.json(); + if (!response.ok) throw new Error(String(body.error || "WebDAV failed")); + if (action === "restore") previewBackup(body.backup); + if (action === "backup") { + try { + localStorage.setItem("dshm-webdav-last", String(Date.now())); + } catch {} + setBackupMessage(t("backupDone")); + } + }).catch((error) => setBackupMessage(String(error))).finally(() => setBackupBusy(false)); + }, [ + previewBackup, + t, + webdavPassword, + webdavUrl, + webdavUser + ]); + /** Map the server's token-source string to a localized label. */ + const gistSourceLabel = (source) => { + if (source === "token") return t("gistSrcToken"); + if (source === "env") return t("gistSrcEnv"); + if (source === "gh") return t("gistSrcGh"); + return source; + }; + /** Turn any failure (server error, network error, timeout) into a friendly message. */ + const gistErrorMessage = (error) => { + const err = error; + const name = typeof err?.name === "string" ? err.name : ""; + const code = typeof err?.code === "string" ? err.code : ""; + if (code === "timeout" || name === "TimeoutError" || name === "AbortError") return t("gistErrTimeout"); + if (code === "network") return t("gistErrNetwork"); + if (code === "auth") return t("gistErrAuth"); + if (code === "notfound") return t("gistErrNotFound"); + if (code === "rate-limit") return t("gistErrRateLimit"); + if (code === "invalid") return t("gistErrInvalid"); + if (error instanceof TypeError) return t("gistErrNetwork"); + return String(error); + }; + const runGist = (0, react.useCallback)((action) => { + setGistBusy(true); + setGistMessage(null); + setGistOk(false); + setGistResult(null); + setRestoreErrors([]); + setExportError(null); + const body = { + action, + token: gistToken.trim() + }; + if (action === "import") body.gistId = gistId.trim(); + if (action === "export" && gistMode === "update") { + if (gistId.trim() === "") { + setGistBusy(false); + setGistMessage(t("gistErrNoId")); + setGistOk(false); + return; + } + body.gistId = gistId.trim(); + } + if (action === "export") { + const allNames = /* @__PURE__ */ new Set([...Object.keys(installed), ...installedBundles]); + if (!(exportSelection.size === allNames.size && exportSelection.size > 0)) { + body.includeDeps = [...exportSelection]; + if (exportIncludeConfig) body.includeConfig = true; + } + } + fetch(api("/dsh-market/gist"), { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(body), + signal: AbortSignal.timeout(3e4) + }).then(async (response) => { + let body = {}; + try { + body = await response.json(); + } catch {} + if (!response.ok) { + const error = new Error(String(body.error || "Gist failed")); + if (typeof body.code === "string") error.code = body.code; + throw error; + } + if (action === "export") { + setGistResult(body); + const ref = body; + if (typeof ref.gistId === "string" && ref.gistId !== "") { + setGistId(ref.gistId); + setGistMode("update"); + try { + localStorage.setItem("dshm-gist-id", ref.gistId); + } catch {} + } + setGistMessage(t("gistExportDone")); + setGistOk(true); + setExportOpen(false); + } else if (action === "import") previewBackup(body.backup); + else { + const source = typeof body.source === "string" ? body.source : ""; + setGistMessage(t("gistVerifySource").replace("{0}", gistSourceLabel(source))); + setGistOk(true); + } + }).catch((error) => { + const message = gistErrorMessage(error); + setGistMessage(message); + setGistOk(false); + if (action === "export") setExportError(message); + }).finally(() => setGistBusy(false)); + }, [ + exportIncludeConfig, + exportSelection, + gistId, + gistToken, + installed, + installedBundles, + previewBackup, + t + ]); + /** The picker list: dependency plugins + bundle-only plugins, deduplicated. */ + const exportOptions = (0, react.useMemo)(() => { + return [.../* @__PURE__ */ new Set([...Object.keys(installed), ...installedBundles])].sort(); + }, [installed, installedBundles]); + /** Classify an install spec for the export picker badge. */ + const specKind = (spec) => { + if (spec === void 0) return "bundle"; + if (/^file:/i.test(spec)) return "file"; + if (/^(github:|git\+|git:)/i.test(spec)) return "git"; + return "npm"; + }; + const openExportPicker = (0, react.useCallback)(() => { + const names = /* @__PURE__ */ new Set([...Object.keys(installed), ...installedBundles]); + setExportSelection(new Set(names)); + setExportIncludeConfig(false); + setExportOpen(true); + }, [installed, installedBundles]); + (0, react.useEffect)(() => { + try { + localStorage.setItem(WEBDAV_STORAGE_KEY, JSON.stringify({ + url: webdavUrl, + username: webdavUser, + auto: autoBackup + })); + } catch {} + if (!autoBackup || webdavUrl.trim() === "") return; + let last = 0; + try { + last = Number(localStorage.getItem("dshm-webdav-last")) || 0; + } catch {} + if (Date.now() - last >= 864e5) runWebdav("backup"); + }, [ + autoBackup, + runWebdav, + webdavUrl, + webdavUser + ]); + const sessionPendingRestart = doneUrls.length + updatedNames.length + removedCount + toggleRestart + (backupRestored ? 1 : 0); + /** + * Plugins the HOST reports as restart-pending, independent of what this + * browser session happens to remember. Installing and then reloading the + * page used to leave no restart affordance at all: the banner is built + * from session state, while the Installed tab only says "activates on + * restart" in passing — so the user was told a restart was needed and + * given nothing to press. Dismissible, because a standing banner nobody + * wants to act on right now is just noise (it returns next session, or + * as soon as another change lands). + */ + const hostPendingNames = Object.keys(activations).filter((name) => activations[name]?.state === "restart"); + const showHostPending = hostPendingNames.length > 0 && !restartNoticeDismissed && sessionPendingRestart === 0; + const pendingRestart = sessionPendingRestart > 0 ? sessionPendingRestart : showHostPending ? hostPendingNames.length : 0; + const displayedInstalled = pendingBackup === null ? installed : { + ...pendingDependencies, + ...installed + }; + const missingRestoreCount = Object.keys(pendingDependencies).filter((name) => !installedFiles.includes(name)).length; + const hasUpdates = reminderUpdatableNames.length > 0; + /** Live status line: structured phase, or the human-line fallback. */ + const phasePart = progressPhase != null ? phaseLabel(progressPhase, t) + (progressCurrent !== null ? " · " + progressCurrent : "") + (progressDone > 0 ? " · " + t("packagesDone").replace("{0}", String(progressDone)) : "") : progressLine || t("progressHint"); + const progressText = cancelling ? t("cancelling") + " · " + phasePart : phasePart; + /** Whether the catalog carries ANY theme-category entry at all — distinct + * from `themePlugins.length === 0` above (which also fires the instant a + * search/sort/time filter matches nothing) so the two empty states read + * differently: "there's nothing here yet" vs "nothing matches your filter". */ + const anyThemePlugins = data === null ? [] : themePlugins(data.plugins); + /** The catalog entry a deprecated plugin's `replacement` names, if any. */ + const replacementOf = (p) => p.deprecated === true && p.replacement !== void 0 ? data?.plugins.find((r) => r.name === p.replacement) : void 0; + const pluginCard = (p) => { + const desc = p.description && (p.description[lang] || p.description.en) || ""; + const done = doneUrls.includes(p.url) || hotUrls.includes(p.url); + const already = isInstalled(p, catalogInstalled, repoIdentities, data?.plugins, repoHints); + const busy = busyUrl === p.url; + const replacement = replacementOf(p); + const record = recordForUrl(records, p.url); + const blocked = record !== null && (record.state === "input" || record.state === "failed"); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: blocked ? `${Market_module_css_default.card} ${Market_module_css_default.cardBlocked}` : Market_module_css_default.card, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.row1, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + style: { minWidth: 0 }, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", { + className: `${Market_module_css_default.nm} ${Market_module_css_default.nmLink}`, + href: p.url, + target: "_blank", + rel: "noreferrer", + title: p.name, + "aria-label": `${p.name} — ${t("repoLink")}`, + children: [ + pluginName(p.name), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GithubRepoMark, { className: Market_module_css_default.repoMark }), + p.deprecated === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.depBadge, + children: t("deprecatedBadge") + }) + ] + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.byline, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OwnerAvatar, { + name: p.name, + owner: p.owner || "" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.owner, + title: p.owner, + children: p.owner + }), + typeof p.downloads === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, { + label: String(p.downloads), + side: "top", + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.star, + children: "· ↓ " + formatCount(p.downloads) + }) + }), + typeof p.stars === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, { + label: String(p.stars), + side: "top", + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.star, + children: "· ★ " + formatCount(p.stars) + }) + }) + ] + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.cardAction, + children: done ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.okState, + children: t("installedBadge") + }) : already ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.okState, + children: t("alreadyInstalled") + }) : busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + className: Market_module_css_default.installBtn, + disabled: true, + children: t("installing") + }) : blocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { + type: "button", + className: Market_module_css_default.cardBlockedMark, + onClick: openOperations, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { size: 13 }), t("opBlockedCard")] + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + className: Market_module_css_default.installBtn, + disabled: busyUrl !== null || !envReady, + onClick: () => setConfirming(p), + children: t("install") + }) + }) + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardDesc, { + text: desc, + t + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardShot, { + plugin: p, + onOpen: openLightbox + }), + p.deprecated === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.deprecate, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.depLine, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["⚠️ ", t("deprecatedWarn")] }), replacement !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.src, + href: replacement.url, + target: "_blank", + rel: "noreferrer", + children: t("replacementHint") + " " + replacement.name + })] + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.foot, + children: [ + pluginCategories(p).map((category) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.tag, + children: data.categories[category] && (data.categories[category][lang] || data.categories[category].en) || category + }, category)), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + className: Market_module_css_default.commentsLink, + onClick: () => setCommentsFor(p), + children: t("comments") + }) + ] + }), + busy && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.progress, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spin, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutline16, { size: 14 }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.grow, + children: progressText + }), + progressPct !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.pct, + children: [progressPct, "%"] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: cancelling, + onClick: doCancel, + children: cancelling ? t("cancelling") : t("cancelOp") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.bar, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: progressPct !== null ? Market_module_css_default.barFill : `${Market_module_css_default.barFill} ${Market_module_css_default.barWave}`, + style: progressPct !== null ? { width: `${progressPct}%` } : void 0 + }) + }) + ] + }) + ] + }, p.url); + }; + const installedNameOf = (p) => matchInstalledName(p, installed, repoIdentities, data?.plugins, repoHints); + const bootEntries = typeof window !== "undefined" && window.__DSH_BOOT__ && Array.isArray(window.__DSH_BOOT__.entries) ? window.__DSH_BOOT__.entries : []; + const themePluginCard = (p) => { + const instName = installedNameOf(p); + const desc = p.description && (p.description[lang] || p.description.en) || ""; + const replacement = replacementOf(p); + const done = doneUrls.includes(p.url) || hotUrls.includes(p.url); + const busy = busyUrl === p.url; + const record = recordForUrl(records, p.url); + const blocked = record !== null && (record.state === "input" || record.state === "failed"); + const mounted = instName !== null && (skins.includes(instName) || bootEntries.some((e) => e.id === instName)) && !effectiveDisabledSet.has(instName); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", { + className: blocked ? `${Market_module_css_default.themeCard} ${Market_module_css_default.cardBlocked}` : Market_module_css_default.themeCard, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThemeCover, { + plugin: p, + onOpen: openLightbox, + t + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.themeCardBody, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.themeCardHead, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.themeIdentity, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", { + className: `${Market_module_css_default.nm} ${Market_module_css_default.nmLink}`, + href: p.url, + target: "_blank", + rel: "noreferrer", + title: p.name, + "aria-label": `${p.name} — ${t("repoLink")}`, + children: [pluginName(p.name), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GithubRepoMark, { className: Market_module_css_default.repoMark })] + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.byline, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OwnerAvatar, { + name: p.name, + owner: p.owner || "" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.owner, + title: p.owner, + children: p.owner + }), + typeof p.downloads === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, { + label: String(p.downloads), + side: "top", + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.star, + children: "· ↓ " + formatCount(p.downloads) + }) + }), + typeof p.stars === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, { + label: String(p.stars), + side: "top", + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.star, + children: "· ★ " + formatCount(p.stars) + }) + }) + ] + })] + }), + p.deprecated === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.depBadge, + children: t("deprecatedBadge") + }), + mounted && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.themeStatus, + children: t("themeActive") + }), + instName !== null && !mounted && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.themeStatusMuted, + children: effectiveDisabledSet.has(instName) ? t("disabledState") : t("alreadyInstalled") + }) + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.themeDescription, + title: desc, + children: desc + }), + p.deprecated === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.deprecate, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.depLine, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["⚠️ ", t("deprecatedWarn")] }), replacement !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.src, + href: replacement.url, + target: "_blank", + rel: "noreferrer", + children: t("replacementHint") + " " + replacement.name + })] + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.themeCardFooter, + children: [instName === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.themeLifecycle, + children: done ? t("installedBadge") : t("notInstalled") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.themeActions, + children: instName === null ? done ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.okState, + children: t("installedBadge") + }) : busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + className: Market_module_css_default.installBtn, + disabled: true, + children: t("installing") + }) : blocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { + type: "button", + className: Market_module_css_default.cardBlockedMark, + onClick: openOperations, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { size: 13 }), t("opBlockedCard")] + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + className: Market_module_css_default.installBtn, + disabled: busyUrl !== null || !envReady, + onClick: () => setConfirming(p), + children: t("install") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [removingName === instName ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: true, + children: t("uninstalling") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => setRemoveConfirm(instName), + children: t("uninstall") + }), mounted ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: togglingName !== null, + onClick: () => doToggle(instName, false, true), + children: t("themeDeactivate") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + onClick: () => doUseSkin(instName), + children: t("themeApply") + })] }) + })] + }), + busy && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.progress, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spin, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutline16, { size: 14 }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.grow, + children: progressText + }), + progressPct !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.pct, + children: [progressPct, "%"] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: cancelling, + onClick: doCancel, + children: cancelling ? t("cancelling") : t("cancelOp") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.bar, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: progressPct !== null ? Market_module_css_default.barFill : `${Market_module_css_default.barFill} ${Market_module_css_default.barWave}`, + style: progressPct !== null ? { width: `${progressPct}%` } : void 0 + }) + }) + ] + }) + ] + })] + }, p.url); + }; + const themeCard = (id, label, swatch) => { + const active = themeSnap !== null && themeSnap.preference === id; + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.card, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.swatches, + children: swatch.map((c, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", { style: { background: c } }, i)) + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.foot, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.nm, + children: label + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.okState, + children: t("themeActive") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + onClick: () => { + try { + props.theme.setTheme(id); + } catch (error) { + setInstallError(String(error)); + } + }, + children: t("themeApply") + }) + ] + })] + }, "th-" + id); + }; + const categories = data === null ? [] : Object.keys(data.categories); + (0, react.useLayoutEffect)(() => { + setVisibleCats(null); + setVisibleCatsOneRow(null); + }, [lang, categories.length]); + (0, react.useLayoutEffect)(() => { + if (catsOpen || visibleCats !== null) return; + const el = catsWrapRef.current; + if (el === null) return; + const chips = [...el.children].filter((c) => c.dataset?.chip === "1"); + if (chips.length === 0) return; + const first = chips[0]; + const rowThreeTop = first.offsetTop + (first.offsetHeight + 6) * 2 - 3; + let fits = 0; + for (const chip of chips) if (chip.offsetTop < rowThreeTop) fits += 1; + setVisibleCats(fits >= chips.length ? fits : Math.max(1, fits - 1)); + const rowTwoTop = first.offsetTop + first.offsetHeight + 6 - 3; + let fitsOneRow = 0; + for (const chip of chips) if (chip.offsetTop < rowTwoTop) fitsOneRow += 1; + setVisibleCatsOneRow(fitsOneRow >= chips.length ? fitsOneRow : Math.max(1, fitsOneRow - 1)); + }, [ + catsOpen, + visibleCats, + data + ]); + (0, react.useEffect)(() => { + if (catsSentinel === null || typeof IntersectionObserver === "undefined") return; + const observer = new IntersectionObserver(([entry]) => { + const leftView = entry !== void 0 && !entry.isIntersecting; + const root = bodyRef.current; + const wrap = catsWrapRef.current; + if (leftView && root !== null && wrap !== null) { + if (root.scrollHeight - root.clientHeight <= wrap.offsetHeight) return; + } + setCatsStuck(leftView); + }, { + root: bodyRef.current, + threshold: 0 + }); + observer.observe(catsSentinel); + return () => observer.disconnect(); + }, [catsSentinel]); + /** + * Becoming stuck auto-collapses an open row — a REAL `catsOpen` flip, not + * a display-only override. An earlier version faked this by computing a + * separate "effectively open" value for rendering while leaving `catsOpen` + * itself true; the chevron's own click handler only ever toggled the real + * `catsOpen`, so while stuck it flipped a value the render path had + * already stopped consulting — clicking "expand" did nothing visible + * (reported: "吸顶滚动了之后,展开没反应了"). Driving the same state the + * chevron drives means the chevron always works, stuck or not. + */ + const catsAutoCollapsedRef = (0, react.useRef)(false); + (0, react.useLayoutEffect)(() => { + if (catsStuck) { + if (catsOpen) { + setCatsOpen(false); + catsAutoCollapsedRef.current = true; + } + } else if (catsAutoCollapsedRef.current) { + setCatsOpen(true); + catsAutoCollapsedRef.current = false; + } + }, [catsStuck]); + /** + * A fresh install (hotUrls/hotNames) and a toggle/group action + * (refreshNames) both end in the same place — "reload the page" — and + * used to render as two near-identical banners stacked on top of each + * other when both happened in one session (reported as "为啥有三个状态横幅 + * 啊,太奇怪了"). They're merged into one count and one banner; only the + * restart banner (a full host restart, a different action entirely) stays + * separate. + */ + const pendingRefreshNames = (0, react.useMemo)(() => [.../* @__PURE__ */ new Set([...hotNames, ...refreshNames])], [hotNames, refreshNames]); + /** Installed plugins the market itself cannot group (#60). */ + const groupableNames = Object.keys(installed).filter((name) => name !== "dsh-market" && name !== "dshmarket"); + /** Names already inside some group; everything else shows under "ungrouped". */ + const groupedNames = (0, react.useMemo)(() => new Set(Object.values(groups).flat()), [groups]); + const ungroupedNames = groupableNames.filter((name) => !groupedNames.has(name)); + /** Installed package names the catalog classifies as themes (client-side + * mirror of the server's classification; themes are exclusive per group). */ + const installedThemeNames = (0, react.useMemo)(() => { + const names = /* @__PURE__ */ new Set(); + if (data === null) return names; + for (const [name, spec] of Object.entries(installed)) { + const entry = entryForDep(data.plugins, name, String(spec), repoIdentities[name], repoHints[name]); + if (entry !== void 0 && pluginCategories(entry).includes("theme")) names.add(name); + } + return names; + }, [ + data, + installed, + repoIdentities, + repoHints + ]); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.root, + "data-dsh-market-root": true, + "data-dsh-market-tab": tab, + "data-dsh-market-fullscreen": tab === "themes" && themesFullscreen ? "true" : void 0, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.head, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.titleRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketLogo, { + size: 22, + style: { flexShrink: 0 } + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { + className: Market_module_css_default.title, + children: t("nav") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.repoLink, + href: "https://github.com/dsh-market/dsh-market", + target: "_blank", + rel: "noreferrer", + title: "dsh-market · GitHub", + children: "dsh-market" + }), + version !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.version, + title: t("versionHint"), + children: ["v", version] + }), + (() => { + const self = installed["dshmarket"] !== void 0 ? "dshmarket" : "dsh-market"; + const status = updates[self]; + return status && status.updateAvailable && !updatedNames.includes(self) && !ignoredUpdateSet.has(self) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: updatingName !== null || busyUrl !== null, + onClick: () => { + setTab("installed"); + if (status.restoreRequired === true) askRestore(self); + else doUpdate(self); + }, + children: updatingName === self ? t("updating") : status.restoreRequired === true ? t("restoreOnline") : t("marketUpdate") + }); + })(), + reminderBatchUpdatableNames.length >= 2 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: updatingAll || updatingName !== null || busyUrl !== null || removingName !== null, + onClick: () => { + setTab("installed"); + doUpdateAll(); + }, + children: updatingAll ? t("updating") : t("updateAll") + " (" + reminderBatchUpdatableNames.length + ")" + }), + bootId !== null && reminderUpdateNames.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => ignoreUpdateNotices(reminderUpdateNames), + children: t("ignoreAllUpdateNotices") + }) + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.sub, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("subtitle") }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.submitLink, + href: "https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/blob/main/contributing.md", + target: "_blank", + rel: "noreferrer", + children: t("submitPlugin") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + className: Market_module_css_default.exportLogBtn, + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, { size: 14 }), + disabled: exportState === "busy", + onClick: doExportLog, + children: exportState === "busy" ? t("exportingLog") : t("exportLog") + }) + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.tabs, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + className: tab === "discover" ? `${Market_module_css_default.tab} ${Market_module_css_default.on}` : Market_module_css_default.tab, + onClick: () => setTab("discover"), + children: t("tabDiscover") + }), + themeSnap !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + className: tab === "themes" ? `${Market_module_css_default.tab} ${Market_module_css_default.on}` : Market_module_css_default.tab, + onClick: () => setTab("themes"), + children: t("tabThemes") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { + className: tab === "installed" ? `${Market_module_css_default.tab} ${Market_module_css_default.on}` : Market_module_css_default.tab, + onClick: () => { + setTab("installed"); + refreshInstalled(true); + }, + children: [t("tabInstalled") + (installedOtherCount > 0 ? " (" + installedOtherCount + ")" : ""), hasUpdates && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: "error", + size: 7, + className: Market_module_css_default.dot + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + className: tab === "backup" || tab === "diagnostics" ? `${Market_module_css_default.tab} ${Market_module_css_default.on}` : Market_module_css_default.tab, + onClick: () => { + if (tab !== "backup" && tab !== "diagnostics") setTab("backup"); + }, + children: t("tabAdvanced") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OperationsPanel, { + t, + describe: describePlugin, + records, + open: operationsOpen, + onOpenChange: setOperationsOpen, + replacing, + envReady, + onClearSettled: () => setRecords((list) => clearSettled(list)), + onCancel: () => doCancel(), + onDismiss: (record) => setRecords((list) => drop(list, record.id)), + onRefresh: () => location.reload(), + onResolveConflict: resolveConflict, + onApproveBuilds: (record) => { + const names = record.blockedBuilds ?? []; + if (names.length === 0) return; + setRecords((list) => drop(list, record.id)); + const plugin = record.url === void 0 ? void 0 : data?.plugins.find((p) => p.url === record.url); + approveAndRetry(names, () => { + if (plugin !== void 0) doInstall(plugin); + else doUpdate(record.name, false, false); + }); + } + }) + ] + }), + (tab === "backup" || tab === "diagnostics") && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.subTabs, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + className: tab === "backup" ? `${Market_module_css_default.tab} ${Market_module_css_default.on}` : Market_module_css_default.tab, + onClick: () => setTab("backup"), + children: t("tabBackup") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + className: tab === "diagnostics" ? `${Market_module_css_default.tab} ${Market_module_css_default.on}` : Market_module_css_default.tab, + onClick: () => setTab("diagnostics"), + children: t("tabDiagnostics") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }) + ] + }), + !envReady && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.banner, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCordisPluginOutline14, { + size: 14, + className: Market_module_css_default.bannerIcon + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.grow, + children: envFailed ? t("envFixFail") : t("envMissing") + }), + !envFailed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: envFixing, + onClick: fixEnv, + children: envFixing ? t("envFixing") : t("envFix") + }) + ] + }), + backupMessage !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.backupMessage, + children: backupMessage + }), + restoreErrors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.banner, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { + size: 14, + className: Market_module_css_default.bannerIcon + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.grow, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("restorePartial") }) }), restoreErrors.map((error) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: error + }, error))] + })] + }), + tab === "installed" && pendingBackup !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.banner, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline14, { + size: 14, + className: Market_module_css_default.bannerIcon + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.grow, + children: t("restoreMissing").replace("{0}", String(missingRestoreCount)) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: backupBusy, + onClick: () => setRestoreConfirmOpen(true), + children: backupBusy ? t("backupWorking") : t("restoreStart") + }) + ] + }), + pendingRefreshNames.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.banner, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSparkle16, { + size: 14, + className: Market_module_css_default.bannerIcon + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.grow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: pendingRefreshNames.length }), + " ", + t("refreshBanner") + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + onClick: () => { + if (hotNames.length > 0) sessionStorage.setItem("dshm-toast", JSON.stringify(hotNames)); + sessionStorage.setItem("dshm-tab", "installed"); + location.reload(); + }, + children: t("refresh") + }) + ] + }), + pendingRestart > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.banner, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline14, { + size: 14, + className: Market_module_css_default.bannerIcon + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.grow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: pendingRestart }), + " ", + t("restartBanner") + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, { + label: debuggerLatch !== null ? t("restartHintDebugged") : supervisor === null ? t("restartHint") : t("restartHintSupervised").replace("{0}", supervisor), + side: "bottom", + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.bannerHint, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQuestionOutline14, { size: 14 }) + }) + }), + restartEnabled && debuggerLatch === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: restarting || hostBusy || busyUrl !== null || updatingName !== null || removingName !== null, + onClick: doRestart, + children: restarting ? t("restarting") : t("restartNow") + }), + showHostPending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + "aria-label": t("dismissNotice"), + onClick: () => { + setRestartNoticeDismissed(true); + try { + sessionStorage.setItem("dshm-restart-dismissed", String(bootId ?? "")); + } catch {} + }, + children: t("dismiss") + }) + ] + }), + activationWarnings.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.banner, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { + size: 14, + className: Market_module_css_default.bannerIcon + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.grow, + children: activationWarnings.map(({ name, info }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: name }), + " — ", + activationMeta(info.state, t).label, + info.reasons.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.spec, + children: [ + "(", + info.reasons.join(" / "), + ")" + ] + }) + ] }, name)) + })] + }), + tab === "installed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HostDependencyDiagnostics, { + findings: hostDependencyFindings, + t + }) + ] + }), + buildsSkipped !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.banner, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { + size: 14, + className: Market_module_css_default.bannerIcon + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.grow, + children: [ + t("buildsSkipped"), + " ", + buildsSkipped.names.join(", ") + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + size: "sm", + disabled: busyUrl !== null, + onClick: () => { + const { plugin, updateName, names, restore } = buildsSkipped; + setBuildsSkipped(null); + approveAndRetry(names, () => { + if (plugin !== void 0) doInstall(plugin); + else if (updateName !== void 0) doUpdate(updateName, false, restore === true); + }); + }, + children: t("approveBuilds") + }) + ] + }), + compatibilityNotice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.banner, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.grow, + children: [ + compatibilityNotice.risks.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t(compatibilityNotice.rollbackId === void 0 ? "compatRiskBannerNoRollback" : "compatRiskBanner") }), + " ", + compatibilitySummary(compatibilityNotice.risks) + ] }), + compatibilityNotice.shadowedNames !== void 0 && compatibilityNotice.shadowedNames.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + compatibilityNotice.risks.length > 0 && " · ", + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("shadowNameBanner") }), + " ", + shadowSummary(compatibilityNotice.shadowedNames) + ] }), + compatibilityNotice.brokenBundles !== void 0 && compatibilityNotice.brokenBundles.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + (compatibilityNotice.risks.length > 0 || (compatibilityNotice.shadowedNames?.length ?? 0) > 0) && " · ", + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("brokenBundleBanner") }), + " ", + compatibilityNotice.brokenBundles.map((entry) => entry.name).join(", ") + ] }) + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: () => setTab("diagnostics"), + children: t("goDiagnose") + }), + compatibilityNotice.rollbackId === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: compatibilityNotice.rollbackUnavailable ?? t("rollbackUnavailable") }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: rollingBack, + onClick: () => void doRollback(compatibilityNotice.rollbackId), + children: rollingBack ? t("rollingBack") : t("rollbackNow") + }) + ] + }), + installError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.err, + children: [installError, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.staleAction, + children: [ + staleName !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + size: "sm", + onClick: () => doUpdate(staleName, true), + children: t("updateNow") + }), + restoreName !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + size: "sm", + onClick: () => doUpdate(restoreName, false, true), + children: t("restoreContinue") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + size: "sm", + variant: "outline", + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, { size: 14 }), + disabled: exportState === "busy", + onClick: doExportLog, + children: exportState === "busy" ? t("exportingLog") : t("exportLog") + }) + ] + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.body, + ref: bodyRef, + onScroll: (e) => setShowTop(e.currentTarget.scrollTop > 400), + children: tab === "backup" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.backupGrid, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", { + className: Market_module_css_default.backupCard, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("backupLocal") }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("backupHint") }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.backupWarn, + children: t("credsWarning") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.backupActions, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, { size: 14 }), + disabled: backupBusy, + onClick: () => downloadFile(api("/dsh-market/backup"), "dsh-profile-backup.json"), + children: backupBusy ? t("backupWorking") : t("backupDownload") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFolderOpen16, { size: 14 }), + disabled: backupBusy, + onClick: () => fileInputRef.current?.click(), + children: backupBusy ? t("backupWorking") : t("backupImport") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", { + ref: fileInputRef, + type: "file", + accept: "application/json,.json", + className: Market_module_css_default.hiddenFile, + tabIndex: -1, + "aria-hidden": "true", + disabled: backupBusy, + onChange: (event) => { + const file = event.currentTarget.files?.[0]; + event.currentTarget.value = ""; + if (file !== void 0) file.text().then((text) => previewBackup(JSON.parse(text))).catch((error) => setBackupMessage(String(error))); + } + }) + ] + }) + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", { + className: Market_module_css_default.backupCard, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("webdav") }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, { + open: presetOpen, + onClose: () => setPresetOpen(false), + onSelect: (id) => { + const urls = { + jianguoyun: "https://dav.jianguoyun.com/dav/dsh-profile-backup.json", + koofr: "https://app.koofr.net/dav/Koofr/dsh-profile-backup.json", + nextcloud: "https://nextcloud.example/remote.php/dav/files/USERNAME/dsh-profile-backup.json" + }; + if (urls[id] !== void 0) setWebdavUrl(urls[id]); + }, + align: "start", + anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }), + onClick: () => setPresetOpen((o) => !o), + children: t("webdavPreset") + }), + items: [ + { + id: "custom", + label: t("webdavPreset") + }, + { + id: "jianguoyun", + label: "坚果云 / Nutstore" + }, + { + id: "koofr", + label: "Koofr" + }, + { + id: "nextcloud", + label: "Nextcloud" + } + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.backupInput, + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLinkOutline14, { size: 14 }), + type: "url", + value: webdavUrl, + placeholder: t("webdavUrl"), + onChange: (e) => setWebdavUrl(e.target.value) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.backupInput, + autoComplete: "username", + value: webdavUser, + placeholder: t("webdavUser"), + onChange: (e) => setWebdavUser(e.target.value) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.backupInput, + type: "password", + autoComplete: "current-password", + value: webdavPassword, + placeholder: t("webdavPassword"), + onChange: (e) => setWebdavPassword(e.target.value) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.backupActions, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: backupBusy || webdavUrl.trim() === "", + onClick: () => runWebdav("backup"), + children: backupBusy ? t("backupWorking") : t("webdavUpload") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: backupBusy || webdavUrl.trim() === "", + onClick: () => runWebdav("restore"), + children: t("webdavRestore") + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { + className: Market_module_css_default.backupCheck, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", { + type: "checkbox", + checked: autoBackup, + onChange: (e) => setAutoBackup(e.target.checked) + }), t("autoBackup")] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("webdavNote") }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.backupWarn, + children: t("credsWarning") + }) + ] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", { + className: Market_module_css_default.backupCard, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("gist") }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.backupInput, + type: "password", + autoComplete: "off", + value: gistToken, + placeholder: t("gistToken"), + onChange: (e) => setGistToken(e.target.value) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.backupInput, + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLinkOutline14, { size: 14 }), + value: gistId, + placeholder: t("gistId"), + onChange: (e) => setGistId(e.target.value) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.backupActions, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { + className: Market_module_css_default.backupCheck, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", { + type: "radio", + name: "gist-mode", + checked: gistMode === "update", + onChange: () => setGistMode("update") + }), t("gistModeUpdate")] + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { + className: Market_module_css_default.backupCheck, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", { + type: "radio", + name: "gist-mode", + checked: gistMode === "create", + onChange: () => setGistMode("create") + }), t("gistModeCreate")] + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.backupActions, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: gistBusy, + onClick: () => runGist("verify"), + children: gistBusy ? t("backupWorking") : t("gistVerify") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: gistBusy || gistMode === "update" && gistId.trim() === "", + onClick: openExportPicker, + children: gistBusy ? t("backupWorking") : t("gistExport") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: gistBusy || gistId.trim() === "", + onClick: () => runGist("import"), + children: t("gistImport") + }) + ] + }), + gistResult !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { + className: Market_module_css_default.backupCheck, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("gistCreated") }), + " ", + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.src, + href: gistResult.gistUrl, + target: "_blank", + rel: "noreferrer", + children: gistResult.gistUrl + }) + ] + }), + gistMessage !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: gistOk ? Market_module_css_default.backupMessage : Market_module_css_default.backupWarn, + children: gistMessage + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("gistNote") }) + ] + }) + ] + }) : tab === "discover" ? loadError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.empty, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("loadFail") }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.err, + children: loadError + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + className: Market_module_css_default.retryBtn, + onClick: () => { + loadCatalog(); + }, + children: t("loadRetry") + }) + ] + }) : data === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.loading, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.logoMark, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketLogo, { + size: 26, + animated: true + }) + }), t("loading")] + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { ref: setCatsSentinel }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.stickyHead, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.tabSearchRow, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.tabSearch, + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 }), + placeholder: t("searchPh"), + value: q, + onChange: (e) => setQ(e.target.value) + }) + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.cats, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.catsRow, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + ref: catsWrapRef, + className: visibleCats === null ? `${Market_module_css_default.catsWrap} ${Market_module_css_default.catsCollapsed}` : Market_module_css_default.catsWrap, + children: (() => { + const budget = catsStuck ? visibleCatsOneRow : visibleCats; + const ordered = orderedCategories(categories, cat, catsOpen, budget); + const shown = catsOpen || budget === null ? ordered : ordered.slice(0, Math.max(0, budget - 1)); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, { + "data-chip": "1", + active: cat === "all", + onClick: () => setCat("all"), + children: t("all") + " (" + formatCount(data.count) + ")" + }), + shown.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Pill, { + "data-chip": "1", + active: cat === id, + onClick: () => setCat(id), + children: data.categories[id] && (data.categories[id][lang] || data.categories[id].en) || id + }, id)), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + className: Market_module_css_default.catsToggle, + icon: catsOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }), + "aria-label": catsOpen ? t("catsLess") : t("catsMore"), + onClick: () => { + catsAutoCollapsedRef.current = false; + setCatsOpen((o) => !o); + } + }) + ] }); + })() + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterMenu, { + sortField, + sortDir, + timeRange, + onSortField: setSortField, + onSortDir: setSortDir, + onTimeRange: setTimeRange, + t + })] + }) + })] + }), + plugins.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.empty, + children: t("empty") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Masonry, { + items: pagePlugins, + render: pluginCard + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pager, { + currentPage, + totalPages, + pageSize, + onGoToPage: goToPage, + onChangePageSize: changePageSize, + t + })] }) + ] }) : tab === "themes" && themeSnap !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.themeToolbar, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.themeSearch, + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 }), + placeholder: t("searchPh"), + value: qThemes, + onChange: (e) => setQThemes(e.target.value) + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.themeToolbarActions, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterMenu, { + sortField: themeSortField, + sortDir: themeSortDir, + timeRange: themeTimeRange, + onSortField: setThemeSortField, + onSortDir: setThemeSortDir, + onTimeRange: setThemeTimeRange, + t + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, { + label: themesFullscreen ? t("themeExitFullscreen") : t("themeFullscreen"), + side: "top", + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + className: Market_module_css_default.themeFullscreenBtn, + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconFullscreenOutline16, { size: 16 }), + "aria-label": themesFullscreen ? t("themeExitFullscreen") : t("themeFullscreen"), + "aria-pressed": themesFullscreen, + onClick: () => setThemesFullscreen((value) => !value) + }) + })] + })] + }), + (() => { + const extra = themeSnap.themes.filter((def) => def.id !== "light" && def.id !== "dark"); + return extra.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: `${Market_module_css_default.grid} ${Market_module_css_default.themesGrid}`, + children: extra.map((def) => themeCard(def.id, def.id, themeSwatch(def))) + }); + })(), + data === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.loading, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.logoMark, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketLogo, { + size: 26, + animated: true + }) + }), t("loading")] + }) : anyThemePlugins.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.empty, + children: t("themeEmpty") + }) : themePlugins$1.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.empty, + children: t("empty") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.themeResultBar, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("themeResultCount").replace("{0}", String(themePlugins$1.length)) }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.themeGallery, + children: themePagePlugins.map(themePluginCard) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Pager, { + currentPage: themePagination.currentPage, + totalPages: themePagination.totalPages, + pageSize: themePagination.pageSize, + onGoToPage: themePagination.goToPage, + onChangePageSize: themePagination.changePageSize, + t + }) + ] }) + ] }) : tab === "diagnostics" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Diagnostics, { t }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.viewBar, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + className: installedView === "list" ? `${Market_module_css_default.viewBtn} ${Market_module_css_default.viewOn}` : Market_module_css_default.viewBtn, + onClick: () => setInstalledView("list"), + children: t("tabList") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + className: installedView === "groups" ? `${Market_module_css_default.viewBtn} ${Market_module_css_default.viewOn}` : Market_module_css_default.viewBtn, + onClick: () => setInstalledView("groups"), + children: t("tabGroups") + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.tabSearchRow, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.tabSearch, + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 }), + placeholder: t("searchPh"), + value: qInstalled, + onChange: (e) => setQInstalled(e.target.value) + }) + }), + installedView === "groups" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.groupCreate, + children: creatingGroup ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.inlineInput, + placeholder: t("groupNamePh"), + value: newGroupName, + onChange: (e) => setNewGroupName(e.target.value), + onKeyDown: (e) => { + if (e.key === "Enter") doCreateGroup(); + }, + autoFocus: true + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + onClick: doCreateGroup, + children: t("groupCreate") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => { + setCreatingGroup(false); + setNewGroupName(""); + }, + children: t("cancel") + }) + ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: () => setCreatingGroup(true), + children: t("groupNew") + }) + }), + groupOrder.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.empty, + children: t("noGroups") + }) : groupOrder.map((gid) => { + const members = groups[gid] ?? []; + const sw = groupSwitchState(members, effectiveDisabledSet); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.groupRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.groupHead, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + role: "switch", + "aria-checked": sw === "on" ? true : sw === "off" ? false : "mixed", + "aria-label": (sw !== "on" ? t("enable") : t("disable")) + " " + gid, + className: sw === "on" ? `${Market_module_css_default.switch} ${Market_module_css_default.switchOn}` : sw === "mixed" ? `${Market_module_css_default.switch} ${Market_module_css_default.switchMixed}` : Market_module_css_default.switch, + disabled: togglingName !== null || sw === "empty", + onClick: () => doGroupToggle(gid, sw !== "on"), + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.switchKnob }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.groupName, + children: gid + }), + sw === "mixed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.groupHint, + children: t("groupMixed") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.groupActions, + children: [ + renamingGroup === gid ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.inlineInput, + placeholder: t("groupNamePh"), + value: renamingValue, + onChange: (e) => setRenamingValue(e.target.value), + onKeyDown: (e) => { + if (e.key === "Enter") doRenameGroup(gid); + }, + autoFocus: true + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + onClick: () => doRenameGroup(gid), + children: t("groupRename") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => { + setRenamingGroup(null); + setRenamingValue(""); + }, + children: t("cancel") + }) + ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => { + setRenamingGroup(gid); + setRenamingValue(gid); + }, + children: t("groupRename") + }), + deletingGroup === gid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + className: Market_module_css_default.dangerArmed, + onClick: () => doDeleteGroup(gid), + children: t("groupConfirmDelete") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + className: Market_module_css_default.dangerBtn, + onClick: () => setDeletingGroup(gid), + children: t("groupDelete") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: () => setAddPanel(addPanel !== null && addPanel.group === gid && addPanel.kind === "plugin" ? null : { + group: gid, + kind: "plugin" + }), + children: t("groupAdd") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: members.some((member) => installedThemeNames.has(member)), + onClick: () => setAddPanel(addPanel !== null && addPanel.group === gid && addPanel.kind === "theme" ? null : { + group: gid, + kind: "theme" + }), + children: t("groupAddTheme") + }) + ] + }) + ] + }), + addPanel !== null && addPanel.group === gid && (() => { + const candidates = addPanel.kind === "theme" ? [...installedThemeNames].filter((name) => !members.includes(name)) : groupableNames.filter((name) => !members.includes(name) && !installedThemeNames.has(name)); + return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.groupAddPanel, + children: candidates.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.groupHint, + children: t("groupAddEmpty") + }) : candidates.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.groupMember, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.nm, + children: name + }), + effectiveDisabledSet.has(name) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + children: t("disabledState") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: () => doAddMember(gid, name), + children: addPanel.kind === "theme" ? t("groupAddTheme") : t("groupAdd") + }) + ] + }, name)) + }); + })(), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.groupMembers, + children: [members.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.groupHint, + children: t("groupEmpty") + }), members.map((member) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.groupMember, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.nm, + children: member + }), + effectiveDisabledSet.has(member) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + children: t("disabledState") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + role: "switch", + "aria-checked": !effectiveDisabledSet.has(member), + "aria-label": (effectiveDisabledSet.has(member) ? t("enable") : t("disable")) + " " + member, + className: effectiveDisabledSet.has(member) ? Market_module_css_default.switch : `${Market_module_css_default.switch} ${Market_module_css_default.switchOn}`, + disabled: togglingName !== null, + onClick: () => doToggle(member, effectiveDisabledSet.has(member)), + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.switchKnob }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => doRemoveMember(gid, member), + children: t("groupRemove") + }) + ] + }, member))] + }) + ] + }, gid); + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.sect, + children: t("ungrouped") + }), + ungroupedNames.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.empty, + children: t("installedEmpty") + }) : ungroupedNames.map((name) => { + const entry = data === null ? void 0 : entryForDep(data.plugins, name, String(installed[name]), repoIdentities[name], repoHints[name]); + const off = effectiveDisabledSet.has(name); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.irow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + style: { minWidth: 0 }, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.nm, + children: [name, entry?.deprecated === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.depBadge, + children: t("deprecatedBadge") + })] + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.act, + children: off ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.actWarn, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: "warning", + size: 7 + }), t("disabledState")] + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.actLive, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: "done", + size: 7 + }), t("stateLive")] + }) + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + assignFor === name ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.assignRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", { + className: Market_module_css_default.assignSelect, + value: assignTarget, + onChange: (e) => setAssignTarget(e.target.value), + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { + value: "", + children: t("groupNamePh") + }), groupOrder.map((gid) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { + value: gid, + children: gid + }, gid))] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: assignTarget === "", + onClick: () => doAssign(name), + children: t("groupAssign") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => { + setAssignFor(null); + setAssignTarget(""); + }, + children: t("cancel") + }) + ] + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: groupOrder.length === 0, + onClick: () => { + setAssignFor(name); + setAssignTarget(""); + }, + children: t("groupAssign") + }) + ] + }, "ug-" + name); + }) + ] }) : Object.keys(displayedInstalled).filter((name) => name !== selfName).length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.empty, + children: t("installedEmpty") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Masonry, { + items: Object.entries(displayedInstalled).filter(([name, spec]) => { + if (name === selfName) return false; + const needle = qInstalled.trim().toLowerCase(); + if (needle === "") return true; + if (name.toLowerCase().includes(needle)) return true; + if (String(spec).toLowerCase().includes(needle)) return true; + const entry = data === null ? void 0 : entryForDep(data.plugins, name, String(spec), repoIdentities[name], repoHints[name]); + if (entry !== void 0) { + if ((entry.description && (entry.description[lang] || entry.description.en) || "").toLowerCase().includes(needle)) return true; + if ((entry.owner || "").toLowerCase().includes(needle)) return true; + } + return false; + }), + render: ([name, spec]) => { + const missing = pendingBackup !== null && !installedFiles.includes(name); + const entry = data === null ? void 0 : entryForDep(data.plugins, name, String(spec), repoIdentities[name], repoHints[name]); + const status = updates[name]; + const localDev = /^(?:link|file):/i.test(String(spec)) || status?.kind === "linked"; + const act = activations[name]; + const meta = act !== void 0 ? activationMeta(act.state, t) : null; + const version = status && status.version ? "v" + status.version : ""; + const specText = String(spec); + const specRedundant = version !== "" && /^[\^~]?\d/.test(specText); + const ghSpec = /^github:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:#|$)/.exec(specText); + const repoUrl = entry !== void 0 ? entry.url : ghSpec !== null ? "https://github.com/" + ghSpec[1] : null; + const off = effectiveDisabledSet.has(name); + const toggleable = off || act !== void 0 && (act.state === "live" || act.state === "restart"); + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: missing ? `${Market_module_css_default.irow} ${Market_module_css_default.irowMissing}` : Market_module_css_default.irow, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + style: { minWidth: 0 }, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: `${Market_module_css_default.nm} ${Market_module_css_default.irowName}`, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.irowNameText, + children: repoUrl !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.nameLink, + href: repoUrl + "#readme", + target: "_blank", + rel: "noreferrer", + title: name, + "aria-label": `${name} — ${t("readme")}`, + children: name + }) : name + }), + entry?.deprecated === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.depBadge, + children: t("deprecatedBadge") + }), + version && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.owner, + title: version, + children: version + }) + ] + }), + specRedundant ? null : repoUrl !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: `${Market_module_css_default.spec} ${Market_module_css_default.src}`, + href: repoUrl, + target: "_blank", + rel: "noreferrer", + style: { display: "inline-block" }, + children: specText + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: specText + }), + notingName === name ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.noteEdit, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, { + className: Market_module_css_default.noteInput, + value: noteDraft, + maxLength: 200, + autoFocus: true, + placeholder: t("notePlaceholder"), + onChange: (e) => setNoteDraft(e.target.value), + onKeyDown: (e) => { + if (e.key === "Enter") saveNote(name, noteDraft); + if (e.key === "Escape") setNotingName(null); + } + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: () => saveNote(name, noteDraft), + children: t("noteSave") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => setNotingName(null), + children: t("cancel") + }) + ] + }) : (() => { + const note = notes[name]; + const authored = entry?.description && (entry.description[lang] || entry.description.en) || ""; + const theirs = showTheirs.includes(name); + const shown = note !== void 0 && !theirs ? note : authored; + if (shown === "" && note === void 0) return null; + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: `${Market_module_css_default.desc} ${Market_module_css_default.descTight} ${Market_module_css_default.noteRow}`, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: note !== void 0 && !theirs ? Market_module_css_default.noteMine : void 0, + children: shown + }), + note !== void 0 && authored !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + className: Market_module_css_default.noteToggle, + title: theirs ? t("noteSeeMine") : t("noteSeeTheirs"), + "aria-label": theirs ? t("noteSeeMine") : t("noteSeeTheirs"), + onClick: () => setShowTheirs((list) => theirs ? list.filter((n) => n !== name) : list.concat(name)), + children: theirs ? t("noteMine") : t("noteTheirs") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + className: `${Market_module_css_default.noteToggle} ${Market_module_css_default.noteAction}`, + title: note === void 0 ? t("noteAdd") : t("noteEdit"), + "aria-label": note === void 0 ? t("noteAdd") : t("noteEdit"), + onClick: () => { + setNoteDraft(note ?? ""); + setNotingName(name); + }, + children: note === void 0 ? t("noteAdd") : t("noteEdit") + }) + ] + }); + })(), + status !== void 0 && status.updateAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.noteRow, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + className: Market_module_css_default.notesLink, + onClick: () => openNotes(name, status.current ?? null, status.latest ?? null, repoUrl), + children: `▸ ${t("notesLink")}` + }), bootId !== null && (ignoredUpdateSet.has(name) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.metaInline, + children: t("updateNoticeIgnored") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + className: Market_module_css_default.noteToggle, + "aria-label": `${t("ignoreUpdateNotice")} ${name}`, + onClick: () => ignoreUpdateNotices([name]), + children: t("ignoreUpdateNotice") + }))] + }), + !off && act !== void 0 && meta !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.act, + children: [meta.dot !== "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: meta.dot === "error" ? Market_module_css_default.actBroken : Market_module_css_default.actWarn, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { + state: meta.dot, + size: 7 + }), meta.label] + }), act.state !== "live" && act.reasons.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, { + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQuestionOutline14, { size: 14 }), + title: t("actWhy"), + open: whyOpen === name, + expandable: true, + expandOnRowClick: true, + onToggle: () => setWhyOpen(whyOpen === name ? null : name), + className: Market_module_css_default.actWhy, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: act.reasons.join(" / ") + }) + })] + }), + entry !== void 0 && entry.deprecated === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.deprecate, + style: { marginTop: 8 }, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.depLine, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["⚠️ ", t("deprecatedWarn")] }), entry.replacement !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.src, + children: t("replacementHint") + " " + entry.replacement + })] + }) + }), + updatingName === name && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.progress, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spin, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutline16, { size: 14 }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { + className: Market_module_css_default.grow, + children: progressText + }), + progressPct !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.pct, + children: [progressPct, "%"] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: cancelling, + onClick: doCancel, + children: cancelling ? t("cancelling") : t("cancelOp") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.bar, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: progressPct !== null ? Market_module_css_default.barFill : `${Market_module_css_default.barFill} ${Market_module_css_default.barWave}`, + style: progressPct !== null ? { width: `${progressPct}%` } : void 0 + }) + }) + ] + }) + ] + }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.irowActions, + children: [ + !missing && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.stateTag, + "data-on": off ? "false" : "true", + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.stateDot, + "data-on": off ? "false" : "true" + }), off ? t("disabledState") : t("switchOnLabel")] + }), + toggleable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { + type: "button", + role: "switch", + "aria-checked": !off, + "aria-label": (off ? t("enable") : t("disable")) + " " + name, + className: off ? Market_module_css_default.switch : `${Market_module_css_default.switch} ${Market_module_css_default.switchOn}`, + disabled: togglingName !== null || busyUrl !== null || updatingName !== null || removingName !== null, + onClick: () => doToggle(name, off), + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.switchKnob }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + entry !== void 0 && entry.deprecated === true && entry.replacement !== void 0 && (() => { + const replacement = data?.plugins.find((r) => r.name === entry.replacement); + if (replacement === void 0) return null; + return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: () => { + setCat("all"); + setQ(entry.replacement); + setTab("discover"); + }, + children: t("viewReplacement") + }), !isInstalled(replacement, catalogInstalled, repoIdentities, data?.plugins, repoHints) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + onClick: () => setConfirming(replacement), + children: t("installReplacement") + })] }); + })(), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { + className: Market_module_css_default.irowTrailing, + children: [missing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.metaTag, + children: t("notInstalled") + }) : updatedNames.includes(name) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: `${Market_module_css_default.metaTag} ${Market_module_css_default.metaTagOk}`, + children: act?.state === "live" ? t("updatedLive") : t("updated") + }) : updatingName === name ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + className: Market_module_css_default.warnBtn, + disabled: true, + children: t("updating") + }) : status && status.updateAvailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + className: Market_module_css_default.warnBtn, + disabled: updatingName !== null, + onClick: () => { + if (status.restoreRequired === true) askRestore(name); + else doUpdate(name); + }, + children: status.restoreRequired === true ? t("restoreOnline") : t("update") + }) : localDev ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.metaTag, + title: t("linkedDev"), + children: t("linkedDev") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.metaTag, + title: t("upToDate"), + children: t("upToDate") + }), !missing && name !== "dsh-market" && name !== "dshmarket" && (removingName === name ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + className: Market_module_css_default.dangerBtn, + disabled: true, + children: t("uninstalling") + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [localDev && status?.restoreRequired !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: removingName !== null || busyUrl !== null || updatingName !== null, + onClick: () => askRestore(name), + children: t("restore") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + className: Market_module_css_default.dangerBtn, + disabled: removingName !== null || busyUrl !== null || updatingName !== null, + onClick: () => setRemoveConfirm(name), + children: t("uninstall") + })] }))] + }) + ] + })] + }, name); + } + }) + ] }) + }), + showTop && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, { + label: t("backTop"), + side: "top", + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.top, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + className: Market_module_css_default.topBtn, + "aria-label": t("backTop"), + onClick: () => { + const el = bodyRef.current; + if (el) el.scrollTo({ + top: 0, + behavior: "smooth" + }); + }, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, { size: 16 }) + }) + }) + }), + confirming !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, { + open: true, + onClose: () => { + setConfirming(null); + setCmdOpen(false); + }, + title: t("confirmTitle") + " " + confirming.name + "?", + footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + onClick: () => { + setConfirming(null); + setCmdOpen(false); + }, + children: t("cancel") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + onClick: () => doInstall(confirming), + children: t("confirmInstall") + })] }), + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.byline, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OwnerAvatar, { + name: confirming.name, + owner: confirming.owner || "" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.owner, + title: confirming.owner, + children: confirming.owner + }), + typeof confirming.downloads === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, { + label: String(confirming.downloads), + side: "top", + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.star, + children: "· ↓ " + formatCount(confirming.downloads) + }) + }), + typeof confirming.stars === "number" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, { + label: String(confirming.stars), + side: "top", + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.star, + children: "· ★ " + formatCount(confirming.stars) + }) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }), + pluginCategories(confirming).map((category) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.tag, + children: data.categories[category] && (data.categories[category][lang] || data.categories[category].en) || category + }, category)) + ] + }), + confirming.added && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.metaInline, + children: t("published") + " " + confirming.added + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardDesc, { + text: confirming.description && (confirming.description[lang] || confirming.description.en) || "", + t + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScreenshotStrip, { + plugin: confirming, + onOpen: openLightbox + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, { + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCodeOutline16, { size: 16 }), + title: t("cmdDetails"), + open: cmdOpen, + expandable: true, + expandOnRowClick: true, + onToggle: () => setCmdOpen((o) => !o), + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.cmd, + children: confirming.install + }) + }), + looksTerminal(confirming, lang) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { + className: Market_module_css_default.warnLine, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { + size: 14, + className: Market_module_css_default.bannerIcon + }), + " " + t("terminalWarn") + " ", + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.src, + href: confirming.url + "#readme", + target: "_blank", + rel: "noreferrer", + children: t("readme") + }) + ] + }), + confirming.deprecated === true && (() => { + const replacement = replacementOf(confirming); + return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.deprecate, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.depLine, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["⚠️ ", t("deprecatedWarn")] }), replacement !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.src, + href: replacement.url, + target: "_blank", + rel: "noreferrer", + children: t("replacementHint") + " " + replacement.name + })] + }) + }); + })(), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { + className: Market_module_css_default.modalNote, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { + size: 14, + className: Market_module_css_default.bannerIcon + }), " " + t("confirmWarn")] + }) + ] + }), + commentsFor !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommentsModal, { + name: pluginName(commentsFor.name), + url: commentsFor.url, + lang, + onClose: () => setCommentsFor(null), + t + }, commentsFor.url), + lightbox !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScreenshotLightbox, { + shots: lightbox.shots, + startIndex: lightbox.index, + onClose: () => setLightbox(null), + t + }), + removeConfirm !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, { + open: true, + onClose: () => setRemoveConfirm(null), + title: t("uninstall") + " " + removeConfirm + "?", + description: t("uninstallConfirmDesc"), + footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + onClick: () => setRemoveConfirm(null), + children: t("cancel") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + disabled: removingName !== null, + onClick: () => doUninstall(removeConfirm), + children: t("uninstall") + })] }) + }), + notesFor !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, { + open: true, + onClose: () => setNotesFor(null), + title: notesFor.repoUrl !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.nameLink, + href: notesFor.repoUrl + "#readme", + target: "_blank", + rel: "noreferrer", + children: notesFor.name + }) : notesFor.name, + footer: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + onClick: () => setNotesFor(null), + children: t("cancel") + }), + children: [ + (notesFor.current !== null || notesFor.latest !== null) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.notesRange, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + children: notesFor.current !== null && notesFor.current.length === 40 ? notesFor.current.slice(0, 7) : notesFor.current + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.notesArrow, + children: "→" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + children: notesFor.latest !== null && notesFor.latest.length === 40 ? notesFor.latest.slice(0, 7) : notesFor.latest + }) + ] + }), + notesState === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("loading") + }), + notesState === "fail" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("notesLoadFail") + }), + notesState === "ready" && updateNotes !== null && (updateNotes.kind === "release" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.notesBody, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.notesMeta, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("notesRelease") }), + updateNotes.release.tag !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: " " + updateNotes.release.tag }), + updateNotes.release.publishedAt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: " · " + updateNotes.release.publishedAt.slice(0, 10) }) + ] + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.notesRendered, + children: renderMarkdown(updateNotes.release.body || t("notesNone")) + })] + }) : updateNotes.kind === "commits" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.notesBody, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.notesMeta, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("notesCommits") }) + }), + !updateNotes.commits.found && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.notesMeta, + children: t("notesCommitsRecent") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { + className: Market_module_css_default.notesList, + children: updateNotes.commits.items.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { + className: Market_module_css_default.notesRow, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.notesDate, + children: c.date !== null ? c.date.slice(0, 10) : "" + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.notesMsg, + children: mdInline(c.message) + }), + notesFor.repoUrl !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.notesSha, + href: notesFor.repoUrl + "/commit/" + c.sha, + target: "_blank", + rel: "noreferrer", + title: c.message, + children: c.sha.slice(0, 7) + }) + ] + }, c.sha)) + }) + ] + }) : updateNotes.kind === "npm" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.notesBody, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.notesMeta, + children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("notesNpm") }) + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { + className: Market_module_css_default.notesList, + children: updateNotes.npmTimes.map((v) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { + className: Market_module_css_default.notesRow, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.notesDate, + children: v.date.slice(0, 10) + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", { + className: Market_module_css_default.notesVer, + href: `https://www.npmjs.com/package/${encodeURIComponent(notesFor.name)}/v/${encodeURIComponent(v.version)}`, + target: "_blank", + rel: "noreferrer", + children: v.version + })] + }, v.version)) + })] + }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.spec, + children: t("notesNone") + })) + ] + }), + restoreConfirmOpen && pendingBackup !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, { + open: true, + onClose: () => setRestoreConfirmOpen(false), + title: t("restoreConfirm"), + footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + onClick: () => setRestoreConfirmOpen(false), + children: t("cancel") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + disabled: backupBusy, + onClick: doRestore, + children: t("confirm") + })] }) + }), + exportOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, { + open: true, + onClose: () => setExportOpen(false), + title: t("gistExportSelect"), + description: t("gistExportHint"), + footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + onClick: () => setExportOpen(false), + children: t("cancel") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + disabled: gistBusy || exportSelection.size === 0, + onClick: () => runGist("export"), + children: gistBusy ? t("backupWorking") : t("gistExportGo") + })] }), + children: [exportOptions.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("gistNoPlugins") }), exportOptions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { + className: Market_module_css_default.backupActions, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + size: "sm", + variant: "outline", + onClick: () => setExportSelection(new Set(exportOptions)), + children: t("gistSelectAll") + }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, { + size: "sm", + variant: "outline", + onClick: () => setExportSelection(/* @__PURE__ */ new Set()), + children: t("gistSelectNone") + })] + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { + className: Market_module_css_default.backupCheckList, + children: exportOptions.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { + className: Market_module_css_default.backupCheck, + children: [ + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", { + type: "checkbox", + checked: exportSelection.has(name), + onChange: (e) => { + const next = new Set(exportSelection); + if (e.currentTarget.checked) next.add(name); + else next.delete(name); + setExportSelection(next); + } + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.grow, + children: name + }), + specKind(installed[name]) === "git" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: `${Market_module_css_default.specTag} ${Market_module_css_default.specTagGit}`, + children: "git" + }), + specKind(installed[name]) === "file" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: `${Market_module_css_default.specTag} ${Market_module_css_default.specTagFile}`, + children: t("gistSpecLocal") + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { + className: Market_module_css_default.spec, + title: installed[name], + children: installed[name] ?? t("bundleTag") + }) + ] + }, name)) + }), + /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { + className: Market_module_css_default.backupCheck, + children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", { + type: "checkbox", + checked: exportIncludeConfig, + onChange: (e) => setExportIncludeConfig(e.target.checked) + }), t("gistIncludeConfig")] + }), + exportIncludeConfig && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.backupWarn, + children: t("credsWarning") + }), + exportError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { + className: Market_module_css_default.backupWarn, + children: exportError + }) + ] })] + }), + exportState === "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, { + text: t("exportedLog"), + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { size: 14 }), + onDone: exportToastDone + }), + exportState === "fail" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, { + text: t("exportLogFail"), + icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { size: 14 }), + onDone: exportToastDone + }), + toggled !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, { + text: toggled.name + " " + t(toggled.enabled ? "toastToggledOn" : "toastToggledOff"), + icon: toggled.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, { size: 14 }), + onDone: toggledDone + }) + ] + }); + } + //#endregion + //#region src/client/SettingsCard.tsx + /** + * The market's card on the plugin configuration page (dsh >= 0.1.0-rc.7). + * + * It manages the market ITSELF — version, update, remove — when the current + * profile owns the dependency. A host-provided market keeps only its version + * display and download-region controls. This page is where a user goes to deal + * with a plugin, + * and "which version am I on / update it / get rid of it" is the part of + * that anybody can act on without knowing how DSH is put together. + * + * `allowRestart` deliberately does NOT appear here. It exists for hosts + * where a supervisor (systemd, launchd, pm2, Docker `restart: always`, a + * desktop wrapper) owns the process, and its audience is whoever wrote that + * deployment — a person already editing config, not someone browsing + * settings. As a switch it read as jargon to everyone else, which is worse + * than absent: a control you cannot evaluate is a control you cannot safely + * touch. It remains a config option. + * + * ## Why the chrome is hand-built (again), and why it now matches + * + * The host's own contract is that "a plugin that ships a browser half owns + * its own card" — the plugins tab only lays out a flex column and dispatches + * `settings.plugin.item`. So the container IS ours to draw, and a value + * import from `dsh-client-ui-settings-plugins` would fail the client + * bundle-purity gate anyway. + * + * What the first version got wrong was drawing something of its own + * invention: a flat, always-expanded box next to rows that collapse and + * carry a chevron. The fix is not a different component — `DisclosureRow` is + * 24px chrome for compact flow rows, a different thing — but the same design + * tokens, laid out the way the host lays out `PluginCard`. Classes below + * mirror it one for one, so the market stops looking like it wandered in + * from another product. + */ + /** Keys the market leaves in the browser; cleared when the user purges. */ + const BROWSER_KEYS = ["dshm-webdav", "dshm-gist-id"]; + const CHANNELS = [ + "stable", + "beta", + "dev" + ]; + const asChannel = (value) => CHANNELS.includes(value) ? value : null; + const CHANNEL_LABEL = { + stable: "setChannelStable", + beta: "setChannelBeta", + dev: "setChannelDev" + }; + const CHANNEL_HINT = { + stable: "setChannelStableHint", + beta: "setChannelBetaHint", + dev: "setChannelDevHint" + }; + const REGIONS = ["global", "china"]; + const asRegion = (value) => REGIONS.includes(value) ? value : null; + const REGION_LABEL = { + global: "setRegionGlobal", + china: "setRegionChina" + }; + const REGION_HINT = { + global: "setRegionGlobalHint", + china: "setRegionChinaHint" + }; + /** + * Read the server's answer, taking the list of channels FROM it. + * + * The card does not decide which channels exist: the server is what accepts + * or refuses a selection, so a card drawing its own list could only ever + * disagree with it. + */ + function readStatus(body) { + const offered = (body.channels ?? []).map(asChannel).filter((c) => c !== null); + const regions = (body.regions ?? []).map(asRegion).filter((r) => r !== null); + return { + version: body.version ?? null, + restart: body.restart === true, + channel: asChannel(body.channel) ?? "stable", + channels: offered.length > 0 ? offered : ["stable", "beta"], + region: asRegion(body.region) ?? "global", + regions: regions.length > 0 ? regions : REGIONS, + regionAuto: body.regionAuto === true, + selfManaged: body.selfManaged !== false + }; + } + function readUpdate(own) { + return { + updateAvailable: own.updateAvailable === true, + latest: own.latest ?? null, + channelSwitch: own.channelSwitch ?? null, + restoreRequired: own.restoreRequired === true + }; + } + /** + * Clear the market's browser-side leftovers. + * + * These are the only two things the market keeps in the browser, and the + * server cannot reach either. Neither holds a credential — the WebDAV + * password is never persisted and a Gist token is read from the environment, + * never from disk — so this is tidiness, not a security step, and the copy + * must not imply otherwise. + */ + function clearBrowserState(storage) { + for (const key of BROWSER_KEYS) try { + storage.removeItem(key); + } catch {} + } + function SettingsCard({ t, onRemoved }) { + const [open, setOpen] = (0, react.useState)(false); + const [status, setStatus] = (0, react.useState)(null); + const [update, setUpdate] = (0, react.useState)(null); + const [phase, setPhase] = (0, react.useState)("idle"); + const [restoreConfirming, setRestoreConfirming] = (0, react.useState)(false); + const [purge, setPurge] = (0, react.useState)(false); + const [error, setError] = (0, react.useState)(null); + /** + * The last self-update was refused by pnpm's fresh-release safety wait + * (#39). Only the market's own card can update the market, so without a + * retry here there is NO way to take a just-published version — the + * discover list's retry button covers every plugin except this one (#255). + */ + const [stale, setStale] = (0, react.useState)(false); + const probed = (0, react.useRef)(false); + (0, react.useEffect)(() => { + if (!open || probed.current) return; + probed.current = true; + let live = true; + (async () => { + try { + const body = await (await fetch(api("/dsh-market/status"), { cache: "no-store" })).json(); + if (live) setStatus(readStatus(body)); + setGithubProxy(typeof body.githubProxy === "string" ? body.githubProxy : null); + } catch { + if (live) setStatus({ + version: null, + restart: false, + channel: "stable", + channels: ["stable", "beta"], + region: "global", + regions: REGIONS, + regionAuto: false, + selfManaged: true + }); + } + try { + const body = await (await fetch(api("/dsh-market/updates"), { cache: "no-store" })).json(); + const own = body.updates?.["dshmarket"] ?? body.updates?.["dsh-market"]; + if (live && own !== void 0) setUpdate(readUpdate(own)); + } catch {} + })(); + return () => { + live = false; + }; + }, [open]); + const post = (0, react.useCallback)(async (path, payload) => { + return await (await fetch(path, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(payload) + })).json(); + }, []); + const onUpdate = (0, react.useCallback)((force = false) => { + setRestoreConfirming(false); + setPhase("working"); + setError(null); + setStale(false); + (async () => { + try { + const body = await post(api("/dsh-market/update"), { + name: "dshmarket", + ...update?.restoreRequired === true ? { restore: true } : {}, + ...force ? { force: true } : {} + }); + if (body.ok === true) setPhase("updated"); + else { + setStale(body.stale === true); + setError(body.error ?? t("setSelfFailed")); + setPhase("failed"); + } + } catch (cause) { + setError(cause instanceof Error ? cause.message : String(cause)); + setPhase("failed"); + } + })(); + }, [ + post, + t, + update?.restoreRequired + ]); + const onRemove = (0, react.useCallback)(() => { + setPhase("working"); + setError(null); + (async () => { + try { + const body = await post(api("/dsh-market/self-uninstall"), { + confirm: true, + purge + }); + if (body.ok === true) { + if (purge) clearBrowserState(localStorage); + setPhase("removed"); + onRemoved?.(); + } else { + setError(body.error ?? t("setSelfFailed")); + setPhase("failed"); + } + } catch (cause) { + setError(cause instanceof Error ? cause.message : String(cause)); + setPhase("failed"); + } + })(); + }, [ + onRemoved, + post, + purge, + t + ]); + const busy = phase === "working"; + const version = status?.version ?? null; + const prerelease = version !== null && version.includes("-"); + /** Re-ask what this channel offers; the previous answer was for another one. */ + const refreshUpdate = (0, react.useCallback)(async () => { + const body = await (await fetch(api("/dsh-market/updates") + "?force=1", { cache: "no-store" })).json(); + const own = body.updates?.["dshmarket"] ?? body.updates?.["dsh-market"]; + setUpdate(own === void 0 ? null : readUpdate(own)); + }, []); + /** + * Select a channel — and show the one the SERVER accepted. + * + * This used to move the control first and ignore the answer, which was + * harmless while every channel was permitted. It stopped being harmless + * the moment one of them can be refused: a 403 would have left "dev" + * highlighted on a profile that is not on it. + */ + const onChannel = (0, react.useCallback)((next) => { + setError(null); + (async () => { + try { + const body = await post(api("/dsh-market/channel"), { channel: next }); + if (body.ok !== true) { + setError(body.error ?? t("setSelfFailed")); + return; + } + setStatus((current) => current === null ? current : { + ...current, + channel: asChannel(body.channel) ?? next + }); + await refreshUpdate(); + } catch (cause) { + setError(cause instanceof Error ? cause.message : String(cause)); + } + })(); + }, [ + post, + refreshUpdate, + t + ]); + /** + * Select a download region — and show the one the SERVER accepted, on the + * same reasoning as the channel above. + * + * A hand-made choice retires the one-time notice: the market no longer has + * anything to explain once the user has answered for themselves. + */ + const onRegion = (0, react.useCallback)((next) => { + setError(null); + (async () => { + try { + const body = await post(api("/dsh-market/region"), { region: next }); + if (body.ok !== true) { + setError(body.error ?? t("setSelfFailed")); + return; + } + const accepted = asRegion(body.region) ?? next; + setStatus((current) => current === null ? current : { + ...current, + region: accepted, + regionAuto: false + }); + try { + const status = await (await fetch(api("/dsh-market/status"), { cache: "no-store" })).json(); + setGithubProxy(typeof status.githubProxy === "string" ? status.githubProxy : null); + } catch {} + } catch (cause) { + setError(cause instanceof Error ? cause.message : String(cause)); + } + })(); + }, [post, t]); + /** One label + hint block with an optional action, the host's row shape. */ + const row = (label, hint, action) => (0, react.createElement)("div", { className: Market_module_css_default.setRow }, (0, react.createElement)("div", { className: Market_module_css_default.setLabelBox }, (0, react.createElement)("div", { className: Market_module_css_default.setLabel }, label), (0, react.createElement)("div", { className: Market_module_css_default.setHint }, hint)), action); + const body = phase === "removed" ? row(t("setSelfRemoved"), t("setSelfRemovedHint"), null) : (0, react.createElement)(react.Fragment, null, status?.selfManaged === true ? row(update?.updateAvailable === true && update.latest !== null ? `${t("setSelfUpdateReady")} ${update.latest}` : update?.channelSwitch != null ? `${t("setChannelSwitch")} ${update.channelSwitch}` : t("setSelfUpToDate"), phase === "updated" ? t("setSelfUpdatedHint") : update?.channelSwitch != null ? t("setChannelSwitchHint") : update?.updateAvailable === true ? t("setSelfUpdateHint") : t("setSelfUpToDateHint"), phase === "updated" ? null : update?.updateAvailable === true ? (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + disabled: busy, + onClick: () => { + if (update.restoreRequired) setRestoreConfirming(true); + else onUpdate(); + } + }, update.restoreRequired ? t("restoreOnline") : t("setSelfUpdate")) : update?.channelSwitch != null ? (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + disabled: busy, + onClick: () => onUpdate() + }, t("setChannelSwitch")) : null) : null, status?.selfManaged === true && restoreConfirming ? (0, react.createElement)("div", { className: Market_module_css_default.setConfirm }, (0, react.createElement)("div", { className: Market_module_css_default.setHint }, t("restoreHint")), (0, react.createElement)("div", { className: Market_module_css_default.setActions }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => { + setRestoreConfirming(false); + } + }, t("setSelfCancel")), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + onClick: () => onUpdate() + }, t("restoreContinue")))) : null, status?.selfManaged === true ? row(t("setChannel"), t(CHANNEL_HINT[status.channel]), (0, react.createElement)("div", { className: Market_module_css_default.setSeg }, (status?.channels ?? ["stable", "beta"]).map((id) => (0, react.createElement)("button", { + key: id, + type: "button", + className: status?.channel === id ? `${Market_module_css_default.setSegBtn} ${Market_module_css_default.setSegOn}` : Market_module_css_default.setSegBtn, + disabled: busy || status === null, + title: id === "dev" ? t("setChannelDevHint") : void 0, + onClick: () => { + onChannel(id); + } + }, t(CHANNEL_LABEL[id]))))) : null, row(t("setRegion"), status?.regionAuto === true ? `${t(REGION_HINT[status.region])} ${t("setRegionAuto")}` : t(REGION_HINT[status?.region ?? "global"]), (0, react.createElement)("div", { className: Market_module_css_default.setSeg }, (status?.regions ?? REGIONS).map((id) => (0, react.createElement)("button", { + key: id, + type: "button", + className: status?.region === id ? `${Market_module_css_default.setSegBtn} ${Market_module_css_default.setSegOn}` : Market_module_css_default.setSegBtn, + disabled: busy || status === null, + onClick: () => { + onRegion(id); + } + }, t(REGION_LABEL[id]))))), status?.selfManaged === true ? row(t("setSelfRemove"), t("setSelfRemoveHint"), phase === "confirming" || busy ? null : (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "outline", + size: "sm", + className: Market_module_css_default.setDanger, + disabled: busy, + onClick: () => { + setPhase("confirming"); + } + }, t("setSelfRemove"))) : null, status?.selfManaged === true && (phase === "confirming" || busy) ? (0, react.createElement)("div", { className: Market_module_css_default.setConfirm }, (0, react.createElement)("div", { className: Market_module_css_default.setHint }, t("setSelfConfirm")), (0, react.createElement)("label", { className: Market_module_css_default.setCheck }, (0, react.createElement)("input", { + type: "checkbox", + checked: purge, + onChange: () => { + setPurge(!purge); + } + }), (0, react.createElement)("span", null, t("setSelfPurge"))), (0, react.createElement)("div", { className: Market_module_css_default.setHint }, purge ? t("setSelfPurgeOn") : t("setSelfPurgeOff")), (0, react.createElement)("div", { className: Market_module_css_default.setActions }, busy ? null : (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "ghost", + size: "sm", + onClick: () => { + setPhase("idle"); + setPurge(false); + } + }, t("setSelfCancel")), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + className: Market_module_css_default.setDanger, + disabled: busy, + icon: busy ? (0, react.createElement)("span", { className: Market_module_css_default.spin }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutline16, { size: 16 })) : void 0, + onClick: onRemove + }, busy ? t("setSelfWorking") : t("setSelfRemoveConfirm")))) : null, error !== null ? (0, react.createElement)("div", { className: Market_module_css_default.err }, error) : null, stale ? (0, react.createElement)("div", { className: Market_module_css_default.setActions }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, { + variant: "primary", + size: "sm", + onClick: () => onUpdate(true) + }, t("updateNow"))) : null); + return (0, react.createElement)("div", { className: open ? `${Market_module_css_default.setCard} ${Market_module_css_default.setCardOpen}` : Market_module_css_default.setCard }, (0, react.createElement)("button", { + type: "button", + className: Market_module_css_default.setHeader, + "aria-expanded": open, + onClick: () => { + setOpen(!open); + } + }, (0, react.createElement)("div", { className: Market_module_css_default.setHeadText }, (0, react.createElement)("div", { className: Market_module_css_default.setName }, t("nav"), version !== null ? (0, react.createElement)("span", { className: Market_module_css_default.version }, ` v${version}`) : null, prerelease ? (0, react.createElement)("span", { className: Market_module_css_default.setBetaTag }, t("setChannelBeta")) : null), (0, react.createElement)("div", { className: Market_module_css_default.setDesc }, t("setCardDesc"))), (0, react.createElement)("span", { className: open ? `${Market_module_css_default.setChevron} ${Market_module_css_default.setChevronOpen}` : Market_module_css_default.setChevron }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 }))), open ? (0, react.createElement)("div", { className: Market_module_css_default.setBody }, body) : null); + } + //#endregion + //#region src/client/index.ts + /** + * dsh-market client: registers a "Market" settings section rendering the + * plugin market UI, plus the post-install toast in the shell overlay layer. + * Built by tsdown into the __ModuleLoader__ factory bundle at + * client/client.js; the only externals are the loader module table's react + * entries. + */ + const NS = "dsh-market"; + /** + * Primitives this bundle relies on that did not exist before rc.6. The + * primitives module is host-injected (external at build time), so on an + * older host the module resolves but these named exports are undefined — + * rendering would throw and blank the whole settings dialog. Returning the + * gaps lets apply() skip registration for a clean downgrade instead. + */ + const REQUIRED_PRIMITIVES = [ + "Menu", + "DisclosureRow", + "Tooltip", + "Toast" + ]; + function missingPrimitives(mod, required = REQUIRED_PRIMITIVES) { + return required.filter((name) => mod[name] === void 0); + } + const name = "dsh-market"; + const inject = [ + "slots", + "locale", + "theme" + ]; + function apply(ctx) { + const gaps = missingPrimitives(_deepseek_ai_dsh_client_ui_primitives); + if (gaps.length > 0) { + console.warn("[dsh-market] host ui-primitives missing " + gaps.join(", ") + " — market section disabled (dsh web >= 0.1.0-rc.6 required)"); + return; + } + ctx.effect(() => ctx.locale.register(NS, { + zh, + en + }), "dsh-market: dictionaries"); + const t = ctx.locale.bind(NS); + let retireSection = null; + ctx.slots.inject("settings.section", () => { + const off = ctx.slots.register({ + name: "settings.section", + id: "market", + order: 40, + label: () => t("nav"), + locale: NS, + inject: () => ({ t }) + }, (ownerProps = {}) => (0, react.createElement)(MarketSection, { + t, + locale: ctx.locale, + theme: ctx.theme, + themeStore: { + subscribe: (cb) => ctx.on("theme/change", cb), + getSnapshot: () => ctx.theme.getTheme() + }, + preferredSubsectionId: ownerProps.preferredSubsectionId + })); + if (typeof off === "function") retireSection = off; + return off; + }); + ctx.inject(["settingsScope"], (scoped) => { + scoped.slots.inject("settings.plugin.item", () => scoped.slots.register({ + name: "settings.plugin.item", + key: NS, + locale: NS, + inject: () => ({ t }) + }, () => (0, react.createElement)(SettingsCard, { + t, + onRemoved: () => { + const off = retireSection; + retireSection = null; + off?.(); + } + }))); + }); + const Toast = () => (0, react.createElement)(InstallToast, { t }); + ctx.slots.inject("shell.overlay", () => ctx.slots.register({ + name: "shell.overlay", + id: "dsh-market-toast", + label: () => "dsh-market" + }, Toast)); + } + //#endregion + exports.REQUIRED_PRIMITIVES = REQUIRED_PRIMITIVES; + exports.apply = apply; + exports.inject = inject; + exports.missingPrimitives = missingPrimitives; + exports.name = name; + return module.exports; + } +}); + +//# sourceMappingURL=client.js.map \ No newline at end of file diff --git a/packages/dshmarket/client/client.js.map b/packages/dshmarket/client/client.js.map new file mode 100644 index 000000000..18cff226b --- /dev/null +++ b/packages/dshmarket/client/client.js.map @@ -0,0 +1 @@ +{"version":3,"file":"client.js","names":["useState","Toast","IconSparkle16","useRef","useState","Modal","css","Button","css","useState","IconChevronUpOutline14","IconChevronDownOutline14","Button","IconLoadingOutline16","IconCheckOutline16","IconWarningOutline16","useRef","postJson","useState","useRef","useCallback","css","Button","useState","useRef","useCallback","css","Button","useState","css","IconChevronDownOutline14","IconChevronRightOutline14","useCallback","useMemo","useRef","IconLoadingOutline16","StateDot","Button","IconRefreshOutline14","css","IconWarningOutline16","useState","useMemo","Menu","Button","IconChevronUpOutline14","IconChevronDownOutline14","IconChevronLeftOutline14","IconChevronRightOutline14","IconLoadingOutline16","IconSparkle16","IconSearchOutline16","useCallback","useSyncExternalStore","useRef","createPortal","themePlugins","patchRecord","themePluginsOf","Tooltip","IconDownloadOutline16","StateDot","IconCordisPluginOutline14","IconRefreshOutline14","IconQuestionOutline14","IconFolderOpen16","Input","IconLinkOutline14","Pill","IconFullscreenOutline16","DisclosureRow","Modal","IconCodeOutline16","Toast","IconCheckOutline16","useState","useRef","useCallback","h","css","Fragment","Button","IconLoadingOutline16","IconChevronDownOutline14","primitives","h"],"sources":["../src/client/locales.ts","../src/client/market-data.ts","../src/client/InstallToast.tsx","../src/client/comments.ts","../src/client/CommentsModal.tsx","../src/client/operations.ts","../src/client/OperationsPanel.tsx","../src/client/preset-panel.tsx","../src/client/snapshot-panel.tsx","../src/client/Diagnostics.tsx","../src/client/self-check.ts","../src/client/MarketSection.tsx","../src/client/SettingsCard.tsx","../src/client/index.ts"],"sourcesContent":["/** zh/en dictionaries for the Market settings section and install toast. */\n\nexport const zh = {\n nav: '插件市场',\n setCardDesc: '查看插件市场版本与设置。',\n setSelfUpToDate: '已是最新版本',\n setSelfUpdateReady: '有新版本',\n setSelfUpdateHint: '更新会下载新版本,重启后生效。',\n setSelfUpToDateHint: '',\n setSelfUpdate: '更新',\n setSelfUpdatedHint: '已下载完成。重启 DeepSeek Harness 后新版本才会生效——前端页面会立即更新,服务端不会。',\n setRegion: '下载区域',\n setRegionGlobal: '全球',\n setRegionChina: '中国大陆',\n setRegionGlobalHint: '从 npm 和 GitHub 官方源下载。',\n setRegionChinaHint: '中国大陆访问更快。仅改变下载线路,插件来源不变。',\n setRegionAuto: '已根据网络状况自动选择,可随时切换。',\n setChannel: '更新通道',\n setChannelStable: '稳定版',\n setChannelBeta: 'Beta',\n setChannelStableHint: '仅接收正式发布版本。',\n setChannelBetaHint: '提前获取待验证版本,可能不稳定。仅影响插件市场自身。',\n setChannelDev: '开发版',\n setChannelDevHint: '开发分支构建,未经验证,不建议普通用户使用。',\n setChannelSwitch: '切换至',\n setChannelSwitchHint: '该版本低于当前已安装版本。',\n setSelfRemove: '卸载插件市场',\n setSelfRemoveHint: '从这个 profile 卸载市场。已装的其它插件不受影响。',\n setSelfConfirm: '确定要卸载插件市场吗?卸载后需使用命令行重新安装。',\n setSelfPurge: '同时清除市场自己的数据',\n setSelfPurgeOn: '将删除:市场的停用清单与自定义分组,以及市场写进 profile 补丁文件的停用行——被市场停用的插件将恢复运行。不涉及密码或令牌,市场从不把它们存在磁盘上。',\n setSelfPurgeOff: '保留市场数据。注意:被市场停用的插件会保持停用,且移除后将没有界面可将其重新启用。',\n setSelfRemoveConfirm: '确认卸载',\n setSelfCancel: '取消',\n setSelfWorking: '正在卸载…',\n setSelfRemoved: '已卸载',\n setSelfRemovedHint: '重启 DeepSeek Harness 后完全清理。',\n setSelfFailed: '操作失败',\n versionHint: '插件市场版本 — 反馈问题时请附上',\n subtitle: '发现社区为 DeepSeek Harness 开发的插件',\n submitPlugin: '申请收录插件 ↗',\n descExpand: '展开',\n descCollapse: '收起',\n searchPh: '搜索插件,例如:通知、终端、记忆…',\n tabDiscover: '发现',\n tabInstalled: '已安装',\n tabAdvanced: '高级',\n all: '全部',\n install: '安装',\n installing: '安装中…',\n installedBadge: '✓ 已安装',\n alreadyInstalled: '✓ 已安装',\n restartBanner: '项变更完成,重启 DeepSeek Harness 后生效',\n uninstall: '卸载',\n confirmRemove: '确认卸载?',\n uninstalling: '卸载中…',\n uninstallConfirmDesc: '将从当前 profile 中移除该插件。',\n restartHint: '重启方式:关闭当前 dsh 进程后重新运行(例如 dsh web)',\n restartHintSupervised: '本进程是 {0} 服务的主进程,重启交给它负责——市场自己重启会连带杀掉 cgroup 里的接管进程,服务将起不来。请执行 systemctl restart <你的 unit>。确认你的配置能承受市场自行重启(如 KillMode=process),可在本插件配置里手动打开「允许重启」。',\n restartHintDebugged: '当前 dsh 进程正在被调试器附着。请从 IDE 或终端停止该进程后重新启动,市场不能从 UI 里杀掉正在调试的宿主。',\n confirmTitle: '安装',\n confirmWarn: '插件是社区第三方代码。安装即表示你信任该来源;构建脚本默认被禁止执行。',\n cancel: '取消',\n empty: '没有匹配的插件',\n installedEmpty: '尚未安装社区插件,可前往「发现」页浏览',\n loadFail: '插件目录加载失败,请稍后重试',\n loadRetry: '重试',\n installFail: '安装失败',\n viewSource: '源码',\n refreshBanner: '项变更需刷新页面生效',\n refresh: '刷新页面',\n update: '更新',\n updating: '更新中…',\n updated: '✓ 已更新,重启后生效',\n cancelOp: '取消',\n cancelled: '已取消',\n busyWait: '已有操作正在进行,请等待其完成(同一时间只执行一个安装/更新/卸载)',\n agentBusyUpdate: '有 agent 正在运行,请等待其完成或将其取消后再更新——更新会直接替换插件文件,运行中的 agent 可能中途报错或新旧版本混用。',\n agentBusyInstall: '有 agent 正在运行,请等待其完成或将其取消后再安装——安装会修改插件文件,运行中的 agent 可能中途报错。',\n compatRiskBanner: '检测到兼容性风险,建议重启前到诊断页处理,或一键回滚本次操作。',\n compatRiskBannerNoRollback: '检测到兼容性风险,建议重启前到诊断页处理。',\n shadowNameBanner: '本次操作让同一个插件名在两个层里同时存在,重启后只有一个会生效:',\n brokenBundleBanner: '下列插件的前端文件已损坏、无法解析,刷新后界面可能空白,建议回滚:',\n goDiagnose: '前往诊断页修复',\n rollbackNow: '回滚本次操作',\n rollingBack: '回滚中…',\n rollbackUnavailable: '无法确认更新前的准确来源,自动回滚不可用。',\n approveBuilds: '放行构建脚本并重试',\n buildsSkipped: '该插件需要运行构建脚本才能工作,出于安全考虑已默认阻止。确认来源可信后,可放行并重新安装:',\n restartNow: '立即重启',\n dismiss: '关闭提示',\n dismissNotice: '关闭此提示',\n restarting: '正在重启…',\n restartFail: '重启失败',\n restartTimeout: '等待 DeepSeek Harness 启动超时',\n updateNow: '立即更新',\n updateFail: '更新失败',\n upToDate: '已是最新',\n linkedDev: '本地开发',\n notesLink: '更新内容',\n notesRelease: '版本说明',\n notesCommits: '提交记录',\n notesCommitsRecent: '最近提交(未能对齐你当前安装的版本,仅供参考)',\n notesNpm: '版本发布时间',\n notesNone: '该插件未提供更新说明。',\n notesLoadFail: '暂时读不到更新说明,稍后再试。',\n restore: '恢复',\n restoreOnline: '换用线上版本',\n restoreHint: '会卸载本地版本,重新安装线上版本,并保持更新检测。无法回退,请二次确认。',\n restoreContinue: '继续更新',\n restoreNoCatalog: '精选目录里没有这个插件,无法恢复到线上版本。可以卸载本地版,或继续用本地开发。',\n restoreFail: '恢复失败',\n exportLog: '导出日志',\n exportingLog: '导出中…',\n exportedLog: '日志已导出:dsh-market-log.txt,请将其附在 issue 中',\n exportLogFail: '日志导出失败',\n readme: '使用说明',\n comments: '评论',\n commentsTitle: '评论',\n commentsNote: '评论来自 GitHub Discussions。打开后会连接 giscus.app 与 GitHub;发表评论需要 GitHub 账号。',\n commentsLoading: '正在加载评论…',\n commentsError: '评论加载失败。请检查网络连接后重试。',\n commentsRetry: '重试',\n commentsOnGitHub: '前往 GitHub 查看讨论',\n terminalWarn: '这看起来是终端/命令行插件:安装到网页版 profile 可能不会生效,甚至导致 DeepSeek Harness 无法启动。建议先阅读其使用说明,并按说明安装到对应的 profile。',\n conflictTitle: '无法安装:与已安装的插件冲突',\n conflictBody: '和已安装的插件冲突,只能留一个:',\n conflictReverted: '已自动撤销,未改动任何插件',\n conflictKeep: '保留已安装的插件',\n conflictKeepNote: '',\n conflictSwap: '改为安装这个',\n conflictSwapNote: '被卸载的插件配置会保留,可以再装回来',\n conflictDetails: '详情',\n conflictOutcomeInstall: '安装',\n conflictOutcomeSkip: '不安装',\n conflictOutcomeKeep: '保留',\n conflictOutcomeRemove: '卸载',\n conflictWhy: 'cordis 不接受包含重复条目 id 的插件树,保留该插件将导致 DeepSeek Harness 下次启动失败。',\n opTitle: '任务',\n opInstalling: '正在安装',\n opNeedsYou: '项需处理',\n opQueued: '排队中',\n opQueuedAhead: '前面还有',\n opRunning: '正在处理',\n opNeedsChoice: '无法安装 · 已自动撤销,未改动任何插件',\n opDone: '已完成',\n opDoneRefresh: '已安装 · 刷新页面后生效',\n opLeaveHint: '可离开本页面,完成后将通知你',\n opEmpty: '暂无进行中的操作',\n opEmptyHint: '安装、更新与卸载的进度将显示在这里',\n opClose: '收起',\n opClear: '清空已完成',\n opDequeue: '移出队列',\n opRetry: '重试',\n opKind_install: '安装',\n opKind_update: '更新',\n opKind_uninstall: '卸载',\n opBlockedCard: '无法安装 · 查看详情',\n conflictDeclined: '已选择保留现有插件,未安装',\n conflictReplacing: '正在替换…',\n conflictReplaceFailed: '替换未完成。下列插件已卸载且未自动恢复,请手动确认:',\n lightboxClose: '关闭预览',\n lightboxPrev: '上一张',\n lightboxNext: '下一张',\n envMissing: '安装插件前需要先配置 pnpm 环境',\n envFix: '自动配置',\n envFixing: '正在配置…',\n envFixFail: '自动配置失败。请使用「导出日志」,并将文件附在 issue 中',\n loading: '正在加载插件目录…',\n backTop: '回到顶部',\n confirm: '确认',\n confirmInstall: '确认安装',\n cmdDetails: '安装命令',\n catsMore: '更多分类',\n catsLess: '收起',\n filter: '筛选',\n filterSort: '排序字段',\n filterDir: '排序方向',\n filterTime: '发布时间范围',\n sortDownloads: 'npm 下载量(近 30 天)',\n sortStars: 'Star 数',\n sortAdded: '发布时间',\n sortDesc: '降序',\n sortAsc: '升序',\n sortNewest: '最新',\n sortOldest: '最旧',\n timeAll: '全部时间',\n timeDay: '最近 1 天',\n timeWeek: '最近 7 天',\n timeMonth: '最近 30 天',\n timeQuarter: '最近 90 天',\n timeYear: '最近 1 年',\n published: '发布于',\n prevPage: '上一页',\n nextPage: '下一页',\n firstPage: '首页',\n lastPage: '末页',\n pageInfo: '第 {0} / {1} 页',\n perPage: '每页',\n marketUpdate: '更新插件市场',\n updateAll: '全部更新',\n ignoreUpdateNotice: '本次不再提醒',\n ignoreAllUpdateNotices: '本次全部忽略',\n updateNoticeIgnored: '本次已忽略提醒',\n tabThemes: '主题',\n tabBackup: '备份与恢复',\n backupLocal: '本地文件',\n backupDownload: '导出备份',\n backupImport: '导入并预览',\n backupHint: '仅包含插件清单和 profile 配置,不包含 node_modules;恢复会按清单重新安装插件。',\n webdav: 'WebDAV',\n webdavPreset: '服务商预设',\n webdavUrl: '备份文件 URL',\n webdavUser: '用户名(可选)',\n webdavPassword: '密码(可选)',\n webdavUpload: '上传备份',\n webdavRestore: '从 WebDAV 恢复',\n autoBackup: '每天自动备份(打开市场时)',\n webdavNote: 'WebDAV 地址与用户名仅保存在当前浏览器;密码只存于服务端,每次会话需重新输入。',\n localOnly: 'WebDAV 地址和凭证仅保存在当前浏览器。',\n credsWarning: '请注意:备份包含 profile 配置,以及可能含密钥的文件(config.toml、.env 等)。本地导出的文件会原样包含这些内容,上传 WebDAV 前请确认目标可信。',\n gist: 'GitHub Gist',\n gistToken: 'GitHub token(仅本次会话内存,刷新后需重新输入;留空则使用服务端 DSH_GITHUB_TOKEN 或已登录的 gh CLI)',\n gistId: 'Gist ID 或链接(导出留空则新建私有 Gist)',\n gistVerify: '验证连接',\n gistExport: '导出到 Gist…',\n gistImport: '从 Gist 导入',\n gistExportDone: '已导出到 Gist',\n gistVerifySource: '验证通过(Token 来源:{0})',\n gistSrcToken: '手动输入',\n gistSrcEnv: '环境变量 DSH_GITHUB_TOKEN',\n gistSrcGh: 'gh CLI',\n gistExportSelect: '选择要导出的插件',\n gistExportHint: '全部勾选 = 完整备份(含配置文件);取消勾选 = 仅导出所选插件,可另行勾选「包含配置文件」。',\n gistSelectAll: '全选',\n gistSelectNone: '全不选',\n gistIncludeConfig: '包含配置文件(可能含密钥,请确认)',\n gistNoPlugins: '当前 profile 没有可导出的插件',\n gistSpecLocal: '本地',\n gistExportGo: '导出',\n gistCreated: '已导出:',\n gistErrTimeout: 'GitHub 请求超时,请检查网络后重试',\n gistErrNetwork: '无法连接 GitHub,请检查网络或代理设置',\n gistErrAuth: 'GitHub 认证失败:Token 无效、已过期,或 gh CLI 未登录',\n gistErrNotFound: '未找到该 Gist,请检查 ID 或链接',\n gistErrRateLimit: 'GitHub 限流或 Token 权限不足',\n gistErrInvalid: 'Gist 内容无效或备份格式不受支持',\n gistModeUpdate: '更新到输入框中的 Gist',\n gistModeCreate: '新建一个私有 Gist',\n gistErrNoId: '更新模式需要 Gist ID:请填写,或切换为「新建」',\n gistNote: '与本地导出(本机存档)不同,Gist 用于跨机器同步:导出到私有 Gist 后,可在其他电脑用同一账号导入复用。Token 仅保存在本次会话内存中,绝不写入浏览器存储;已登录 gh CLI 或设置 DSH_GITHUB_TOKEN 则无需输入。Gist 为私有,单文件上限 1 MB。',\n backupWorking: '处理中…',\n backupDone: '备份已上传',\n restoreDone: '恢复完成,请重启 DeepSeek Harness',\n restorePartial: '恢复已完成,但下列插件安装失败:',\n restoreUnportable: '依赖指向另一台机器上的绝对路径,本机不存在。请手动改为本机路径,或重新安装该插件',\n restoreBootError: '恢复后仍无法启动:',\n orphanBundle: '⚠️ 下次启动会失败:profile 声明了这些 bundle,但它们没有安装。请重新安装,或从 profile 的 package.json 的 dsh.profile.bundles 中删除:',\n restoreConfirm: '恢复将覆盖当前 profile 配置并重新安装插件,确定继续吗?',\n restorePreviewDone: '备份已导入,请在「已安装」中确认后开始恢复',\n restoreMissing: '备份中有 {0} 个插件尚未安装',\n restoreStart: '开始恢复',\n notInstalled: '未安装',\n themeApply: '使用',\n themeActive: '使用中',\n themeDeactivate: '停用',\n themeEmpty: '目录中暂无主题',\n themePreview: '预览',\n themePreviewLoading: '正在查找预览…',\n themePreviewMissing: '暂无预览',\n themePreviewCount: '{0} 张预览',\n themeResultCount: '{0} 款主题',\n themeFullscreen: '全屏浏览',\n themeExitFullscreen: '退出全屏',\n progressHint: '首次安装需要下载与解析依赖,大型插件可能需要 1-3 分钟',\n toastReady: '已安装并已生效',\n toastTheme: '已启用。可在 设置 → 插件市场 → 主题 中随时切换',\n toastToggledOn: '已启用',\n toastToggledOff: '已停用,相关功能立即停止',\n gotIt: '关闭',\n stateLive: '已生效',\n stateRestart: '已安装,重启后生效',\n stateInert: '已安装,未生效',\n stateBroken: '已安装,校验未通过',\n stateDisabled: '已停用',\n phaseResolving: '解析依赖',\n phaseDownloading: '下载中',\n phaseLinking: '链接依赖',\n phaseBuilding: '运行构建脚本',\n cancelling: '正在取消…',\n packagesDone: '已处理 {0} 个包',\n updatedLive: '✓ 已更新,已生效',\n partialNote: '已取消,部分变更已写入',\n reconciledNote: '已卸载,但 pnpm 在收尾阶段报错(通常是文件被占用);配置已同步,无需重试',\n actWhy: '为什么未生效?',\n tabList: '列表',\n tabGroups: '分组',\n repoLink: '在 GitHub 上查看仓库',\n disabledState: '已停用',\n noteAdd: '添加备注',\n noteEdit: '编辑备注',\n noteTheirs: '原文',\n noteMine: '我的备注',\n noteSave: '保存',\n notePlaceholder: '用你自己的话描述这个插件,将代替作者简介显示',\n noteSeeTheirs: '查看作者写的简介',\n noteSeeMine: '返回我的备注',\n switchOnLabel: '启用中',\n enable: '启用',\n disable: '停用',\n toggleFail: '切换失败',\n deprecatedBadge: '已废弃',\n deprecatedWarn: '该插件已被目录标记为废弃,不建议新用户安装。',\n viewReplacement: '查看替代品',\n installReplacement: '安装替代品',\n replacementHint: '目录建议改用',\n groupNew: '新建分组',\n groupNamePh: '分组名称',\n groupRename: '重命名',\n groupDelete: '删除分组',\n groupConfirmDelete: '确认删除?',\n ungrouped: '未分组',\n groupAssign: '分配',\n groupRemove: '移出',\n groupEmpty: '该组暂无成员',\n groupMixed: '部分启用',\n noGroups: '尚无分组,请先新建',\n groupCreate: '创建',\n groupAdd: '加入插件',\n groupAddTheme: '加入主题',\n groupAddEmpty: '所有已安装插件都已在该组中',\n tabDiagnostics: '诊断',\n checkIssues: '发现问题',\n checkErrors: '错误',\n checkWarnings: '警告',\n checkLoading: '正在分析 profile…',\n checkLoadFail: '诊断加载失败:',\n checkProfile: 'profile',\n checkErrorsEmpty: '没有错误',\n checkWarningsEmpty: '没有警告',\n checkBundles: '插件加载顺序(bundle)',\n checkBundlesEmpty: '未声明任何插件层',\n checkOfficial: '官方',\n checkCommunity: '社区',\n checkSource: '来源',\n checkEntries: '加载条目(loader id)',\n checkPatch: 'patch 文件',\n checkDir: '目录',\n checkDuplicates: '重复的插件条目',\n checkDuplicatesEmpty: '没有重复的插件条目',\n checkHoisted: '顶层',\n checkPeerMismatches: '依赖版本不匹配',\n checkPeerEmpty: '没有依赖版本不匹配',\n checkPeerInfo: '另有 {0} 条信息(未确认问题)',\n checkPeerOverview: '{0} 不匹配 · {1} 条信息',\n checkRange: '声明范围',\n checkResolved: '实际版本',\n checkSatisfied: '满足',\n checkUnsatisfied: '不满足',\n checkUnknown: '未知',\n checkPeerRisk: '依赖风险',\n checkPeerRiskEmpty: '没有风险级依赖不匹配',\n checkPeerWarning: '依赖警告',\n checkPeerWarningEmpty: '没有警告级依赖不匹配',\n checkPeerInfoTier: '依赖信息',\n checkPeerInfoTierEmpty: '没有信息级依赖条目',\n checkPeerSatisfied: '已满足的依赖',\n checkPeerSatisfiedEmpty: '没有已满足的依赖条目',\n peerRiskBelowMin: '低于声明下界',\n peerRiskAboveMax: '高于显式上界',\n peerWarnOptional: '可选依赖',\n peerWarnAboveMax: '超出声明上界',\n peerInfoUnverified: '未达判定',\n checkMultiVersion: '核心包多版本',\n checkMultiEmpty: '没有多版本核心包',\n checkOverrides: '覆盖关系',\n checkOverridesEmpty: '没有覆盖关系',\n checkOverridden: '覆盖了',\n checkOrphans: '无效的配置条目',\n checkOrphansEmpty: '没有无效配置条目',\n // Orphan patch reason → badge label (issue #98 supplement).\n orphanInsertNotArray: '格式错误',\n orphanInsertTargetMissing: '目标不存在',\n orphanInsertTargetNotGroup: '目标不是分组',\n orphanIdRequired: '缺少标识',\n orphanPatchTargetMissing: '目标不存在',\n orphanNameMismatch: '名称不匹配',\n orphanReasonOther: '其他',\n // Plain-language explainer for the diagnostics page (issue #98 UX).\n diagExplain: '术语说明',\n diagExplainText: '本页检查插件之间的冲突、依赖版本不匹配与加载顺序问题。常用术语:',\n diagTermBundle: 'bundle = 一个插件包,会按顺序叠加加载',\n diagTermEntry: '加载条目 = 插件在运行组合里注册的每一项',\n diagTermPeer: '对等依赖 = 插件要求宿主环境提供的另一个包',\n diagTermShadow: '遮蔽 = 插件自带的旧版包盖住了宿主的新版',\n diagTermOrphan: '无效条目 = 配置里引用了不存在的东西',\n diagTermOrder: '加载顺序 = 插件被激活的先后,后加载者可能覆盖先加载者',\n // issue #98 phase 2 (step 1): minimal community-bundle ordering\n orderSection: '排序',\n orderUp: '↑',\n orderDown: '↓',\n orderApply: '应用顺序',\n orderDrag: '拖拽排序',\n orderDragHint: '拖动 ⠿ 调整顺序,选择「应用顺序」后才会保存',\n orderConflicts: '当前顺序的 before/after 冲突',\n orderApplied: '✓ 已应用,重启后生效',\n orderSuggestApply: '采用建议顺序',\n orderSuggestHint: '建议顺序(按 before/after 规则自动排序):',\n orderAutoSort: '自动排序',\n orderAlreadyOptimal: '当前顺序已满足全部规则与插件依赖,无需调整',\n orderTrialFail: '静态组合校验未通过:{0}',\n orderDiffHint: '该顺序会改变组合:{0} 处覆盖、{1} 个无效条目、{2} 个重复条目',\n duplicateNames: '同名插件(仅信息展示,不视为冲突)',\n orderReset: '重置草稿',\n checkRefresh: '重新检查',\n aiFix: 'AI 修复',\n aiFixHint: '将诊断问题交由新对话中的 Agent 修复(内容已复制到剪贴板,由你决定是否发送)',\n aiFixIntro: '请帮我修复 DeepSeek Harness 的插件问题(profile:{0})。诊断发现如下:',\n aiFixDetect: '【第一步·先判断身份】开始前先判断:当前 Agent 所运行的进程,是否就是这个 profile 对应的 DeepSeek Harness 本机实例(即:诊断出的这套组合,就是你现在运行中的组合)?判断依据:当前工作目录 / $DSH_HOME / 进程命令行是否指向该 profile,或你是否运行在 deepseek-harness 进程内。若无法确定,一律按【是你】处理(更安全)。',\n aiFixIfSelf: '【是 → 你就是这个 harness】严禁原地改动组合、harness 或核心包:不修改 cordis.patch.yml、不调整 bundle 顺序或启停、不安装/卸载/升级插件、不重新安装依赖、不升级或重启 dsh。三步:① 先只读分析,不改任何东西;② 给出 apply.sh/.bat(本次修复的改动)与 rollback.sh/.bat(完整撤销 apply);两者都幂等——apply 仅在无已有快照时才创建快照(否则不覆盖;已存在说明 apply 跑过,先回滚或确认再重跑),rollback 从这份快照恢复,因此 apply 跑到一半失败也能回滚、重复回滚也安全;③ 由我在外部终端执行 apply,并将 stdout/stderr 回贴给你判断。每步先说明理由、等我确认;绝不自行执行。',\n aiFixScope: '【否 → 保守执行】你要是判定自己并非运行在目标 harness 内(独立进程),才可考虑:修改 dsh.profile.bundles 顺序、停用/启用插件、调整 cordis.patch.yml。注意:官方 bundle 不可移动;动手前先说明计划。',\n aiFixConservative: '请保持保守:只修复上面列出的明确错误(启动失败/重复条目/风险级依赖不匹配)。警告和信息级问题(如可选依赖、未确认的依赖范围)仅在它们与明确错误相关时处理。不要做不必要的升级或重新排序;每项改动前说明理由并等待确认。',\n aiFixCopied: '已复制修复提示词,请粘贴到新对话后发送',\n aiFixFail: '无法访问剪贴板,请手动复制下面的诊断内容',\n catConflict: '冲突',\n catDeps: '依赖',\n catRisk: '风险',\n catWarn: '警告',\n catInfo: '信息',\n catOrder: '顺序',\n /** Summary-strip tooltip for the order count (before/after rule conflicts). */\n checkOrderTip: '社区 bundle 加载顺序与插件声明的 before/after 规则冲突',\n diagOkAll: '未发现冲突、依赖或加载顺序问题',\n // issue #98 phase 3: snapshots & rollback panel\n presetSection: '插件组合',\n presetHint: '把当前的 bundle 顺序与停用列表保存为组合,随时切换;应用前会做试启动校验并自动创建快照。',\n presetName: '组合名称',\n presetSave: '保存当前状态',\n presetSaving: '保存中…',\n presetSaved: '组合已保存',\n presetApply: '应用',\n presetApplied: '已应用,重启后生效',\n presetDelete: '删除',\n presetDeleted: '组合已删除',\n presetEmpty: '还没有保存过组合',\n presetNameEmpty: '请输入组合名称',\n presetFail: '组合操作失败:',\n presetListFail: '组合列表加载失败:',\n presetBundleCount: '{0} 个 bundle',\n presetPreview: '预览',\n presetPreviewTitle: '应用此组合将:',\n presetReorder: '重排 {0} 个 bundle 的位置',\n presetEnable: '启用 {0} 个插件',\n presetDisable: '停用 {0} 个插件',\n presetNoop: '无变更',\n presetPreviewFail: '预览失败:',\n snapSection: '快照与回滚',\n snapHint: '应用变更前会自动创建快照;也可以手动创建快照,随时回滚到任意状态。',\n snapCreate: '创建快照',\n snapCreating: '创建中…',\n snapCreated: '快照已创建',\n snapEmpty: '还没有快照',\n snapListFail: '快照列表加载失败:',\n snapCreateFail: '创建快照失败:',\n snapRestore: '回滚',\n snapRestoring: '回滚中…',\n snapRestored: '已回滚,重启后生效',\n snapRestoreFail: '回滚失败:',\n snapRestoreConfirm: '确认回滚',\n snapRestoreConfirmText: '回滚会用快照覆盖当前 profile 的 package.json 与配置,且不可撤销(可先创建新快照再回滚)。',\n snapFiles: '包含文件',\n snapDelete: '删除',\n snapDeleting: '删除中…',\n snapDeleted: '快照已删除',\n snapDeleteFail: '删除快照失败:',\n snapDeleteConfirm: '确认删除',\n snapDeleteConfirmText: '删除后该快照将无法恢复。',\n marketNoToggle: '市场自身不能停用',\n hostDependencyWarning: '插件在 dependencies 中声明了已知的 DSH 共享宿主包,可能遮蔽宿主版本(仅依据清单,未确认运行时重复):',\n hostDependencyMore: '另有 {0} 条,已省略',\n}\n\n/** A dictionary key; zh is the source of truth, en mirrors it 1:1. */\nexport type MarketKey = keyof typeof zh\n\nexport const en: Record = {\n nav: 'Plugin Market',\n setCardDesc: 'View the plugin market version and settings.',\n setSelfUpToDate: 'Up to date',\n setSelfUpdateReady: 'New version available:',\n setSelfUpdateHint: 'Updating downloads the new version; it takes effect after a restart.',\n setSelfUpToDateHint: '',\n setSelfUpdate: 'Update',\n setSelfUpdatedHint: 'Downloaded. Restart DeepSeek Harness for it to take effect — the frontend updates at once, the server does not.',\n setRegion: 'Download region',\n setRegionGlobal: 'Global',\n setRegionChina: 'China mainland',\n setRegionGlobalHint: 'Downloads directly from npm and GitHub.',\n setRegionChinaHint: 'Faster in mainland China. Changes the download route, not the source.',\n setRegionAuto: 'Selected from a network check. Change it anytime.',\n setChannel: 'Update channel',\n setChannelStable: 'Stable',\n setChannelBeta: 'Beta',\n setChannelStableHint: 'Released versions only.',\n setChannelBetaHint: 'Early access to unverified versions. Affects the plugin market only.',\n setChannelDev: 'Dev',\n setChannelDevHint: 'Unverified builds from a development branch. Not recommended for general use.',\n setChannelSwitch: 'Switch to',\n setChannelSwitchHint: 'This version is lower than the one installed.',\n setSelfRemove: 'Uninstall the plugin market',\n setSelfRemoveHint: 'Uninstall the market from this profile. Your other plugins are untouched.',\n setSelfConfirm: 'Uninstall the plugin market? Reinstalling it requires the command line.',\n setSelfPurge: 'Also clear the market\\'s own data',\n setSelfPurgeOn: 'Will delete: the market\\'s disable list and custom groups, and the disable rows it wrote into the profile patch file — plugins the market switched off start running again. No passwords or tokens are involved; the market never stores those on disk.',\n setSelfPurgeOff: 'Keeps the market\\'s data. Note: plugins the market switched off stay off, and once it is removed there is no UI left to switch them back on.',\n setSelfRemoveConfirm: 'Uninstall',\n setSelfCancel: 'Cancel',\n setSelfWorking: 'Uninstalling…',\n setSelfRemoved: 'Uninstalled',\n setSelfRemovedHint: 'Restart DeepSeek Harness to finish cleaning up.',\n setSelfFailed: 'The operation failed',\n versionHint: 'Plugin market version — include it when reporting an issue',\n subtitle: 'Discover community plugins for DeepSeek Harness',\n submitPlugin: 'Submit a plugin ↗',\n descExpand: 'Show more',\n descCollapse: 'Show less',\n searchPh: 'Search plugins: notify, terminal, memory…',\n tabDiscover: 'Discover',\n tabInstalled: 'Installed',\n tabAdvanced: 'Advanced',\n all: 'All',\n install: 'Install',\n installing: 'Installing…',\n installedBadge: '✓ Installed',\n alreadyInstalled: '✓ Installed',\n restartBanner: 'change(s) done — restart DeepSeek Harness to apply',\n uninstall: 'Uninstall',\n confirmRemove: 'Uninstall?',\n uninstalling: 'Uninstalling…',\n uninstallConfirmDesc: 'This removes the plugin from the current profile.',\n restartHint: 'To restart: stop the current dsh process and run it again (e.g. dsh web)',\n restartHintSupervised: 'This process is {0}\\'s own service process, so restarts belong to it — restarting from here would kill the takeover process along with the cgroup and the service would not come back. Use systemctl restart <your unit>. If your unit can survive a self-restart (KillMode=process), turn Allow restart on in this plugin\\'s configuration.',\n restartHintDebugged: 'This dsh process is under a debugger. Stop it from your IDE or terminal and start it again — the market cannot kill a debug-attached host from the UI.',\n confirmTitle: 'Install',\n confirmWarn: 'Plugins are third-party community code. Installing means you trust this source; build scripts are blocked by default.',\n cancel: 'Cancel',\n empty: 'No plugins match',\n installedEmpty: 'No community plugins yet — browse the Discover tab',\n loadFail: 'The plugin catalog failed to load. Try again later.',\n loadRetry: 'Retry',\n installFail: 'Install failed',\n viewSource: 'Source',\n refreshBanner: 'change(s) applied — refresh the page to see them',\n refresh: 'Refresh',\n update: 'Update',\n updating: 'Updating…',\n updated: '✓ Updated — restart to apply',\n cancelOp: 'Cancel',\n cancelled: 'Cancelled',\n busyWait: 'Another operation is already running — please wait for it to finish (one install/update/uninstall at a time)',\n agentBusyUpdate: 'An agent is currently working — wait for it to finish (or cancel it) before updating. Updates replace plugin files in place, so a working agent can fail or mix versions mid-turn.',\n agentBusyInstall: 'An agent is currently working — wait for it to finish (or cancel it) before installing. Installing changes plugin files, so a working agent can fail mid-turn.',\n compatRiskBanner: 'Compatibility risk detected — open Diagnostics before restarting, or roll this operation back.',\n compatRiskBannerNoRollback: 'Compatibility risk detected — open Diagnostics before restarting.',\n shadowNameBanner: 'This operation left one plugin name defined in two layers; only one will load after a restart:',\n brokenBundleBanner: 'These plugins now have a client bundle that will not parse; the page may come up blank after a refresh — rolling back is recommended:',\n goDiagnose: 'Open Diagnostics',\n rollbackNow: 'Roll back',\n rollingBack: 'Rolling back…',\n rollbackUnavailable: 'Automatic rollback is unavailable because the previous exact source could not be verified.',\n approveBuilds: 'Allow build scripts and retry',\n buildsSkipped: 'This plugin needs its build scripts to run; they are blocked by default for safety. If you trust the source, allow them and reinstall:',\n restartNow: 'Restart now',\n dismiss: 'Dismiss',\n dismissNotice: 'Dismiss this notice',\n restarting: 'Restarting…',\n restartFail: 'Restart failed',\n restartTimeout: 'Timed out waiting for DeepSeek Harness to start',\n updateNow: 'Update now',\n updateFail: 'Update failed',\n upToDate: 'Up to date',\n linkedDev: 'local',\n notesLink: 'What changed',\n notesRelease: 'Release notes',\n notesCommits: 'Commits',\n notesCommitsRecent: 'Recent commits (could not be aligned with your installed version — for reference only)',\n notesNpm: 'Version publish times',\n notesNone: 'This plugin does not publish update notes.',\n notesLoadFail: 'Update notes are unavailable right now — try again later.',\n restore: 'Restore',\n restoreOnline: 'Use online version',\n restoreHint: 'This will uninstall the local version, reinstall the catalog version, and keep update checks. This cannot be undone — please confirm again.',\n restoreContinue: 'Continue update',\n restoreNoCatalog: 'This plugin is not in the curated catalog, so it cannot be restored. Uninstall the local copy, or keep developing it locally.',\n restoreFail: 'Restore failed',\n exportLog: 'Export log',\n exportingLog: 'Exporting…',\n exportedLog: 'Log exported: dsh-market-log.txt — please attach it to your issue',\n exportLogFail: 'Log export failed',\n readme: 'README',\n comments: 'Comments',\n commentsTitle: 'Comments',\n commentsNote: 'Comments come from GitHub Discussions. Opening this connects to giscus.app and GitHub; posting needs a GitHub account.',\n commentsLoading: 'Loading comments…',\n commentsError: 'Comments could not be loaded. Check your connection and try again.',\n commentsRetry: 'Try again',\n commentsOnGitHub: 'Read the discussion on GitHub',\n terminalWarn: 'This looks like a terminal/CLI plugin: installing it into the web profile may do nothing, or even break DeepSeek Harness startup. Read its README and install it into the profile it targets.',\n conflictTitle: 'Cannot install: conflicts with an installed plugin',\n conflictBody: 'It conflicts with an installed plugin — only one can stay:',\n conflictReverted: 'Reverted automatically; nothing was changed',\n conflictKeep: 'Keep the installed plugin',\n conflictKeepNote: '',\n conflictSwap: 'Switch to this one',\n conflictSwapNote: 'Settings of the uninstalled plugins are kept, so you can reinstall later',\n conflictDetails: 'Details',\n conflictOutcomeInstall: 'install',\n conflictOutcomeSkip: 'skip',\n conflictOutcomeKeep: 'keep',\n conflictOutcomeRemove: 'uninstall',\n conflictWhy: 'cordis refuses a plugin tree that contains duplicate entry ids; keeping this plugin would stop DeepSeek Harness from starting.',\n opTitle: 'Tasks',\n opInstalling: 'Installing',\n opNeedsYou: 'need attention',\n opQueued: 'Queued',\n opQueuedAhead: 'ahead:',\n opRunning: 'In progress',\n opNeedsChoice: 'Cannot install · reverted automatically, nothing changed',\n opDone: 'Done',\n opDoneRefresh: 'Installed · refresh the page to apply',\n opLeaveHint: 'You can leave this page; you will be notified when it finishes',\n opEmpty: 'No operations in progress',\n opEmptyHint: 'Install, update and uninstall progress appears here',\n opClose: 'Collapse',\n opClear: 'Clear finished',\n opDequeue: 'Remove from queue',\n opRetry: 'Retry',\n opKind_install: 'Install',\n opKind_update: 'Update',\n opKind_uninstall: 'Uninstall',\n opBlockedCard: 'Cannot install · details',\n conflictDeclined: 'You kept the installed plugins; this one was not installed',\n conflictReplacing: 'Replacing…',\n conflictReplaceFailed: 'Replace did not finish. The plugins below were uninstalled and not restored — please check them:',\n lightboxClose: 'Close preview',\n lightboxPrev: 'Previous',\n lightboxNext: 'Next',\n envMissing: 'pnpm needs to be set up before installing plugins',\n envFix: 'Set up automatically',\n envFixing: 'Setting up…',\n envFixFail: 'Automatic setup failed — export the log and attach it to your issue',\n loading: 'Loading the catalog…',\n backTop: 'Back to top',\n confirm: 'Confirm',\n confirmInstall: 'Confirm install',\n cmdDetails: 'Install command',\n catsMore: 'More',\n catsLess: 'Less',\n filter: 'Filter',\n filterSort: 'Sort field',\n filterDir: 'Order',\n filterTime: 'Released within',\n sortDownloads: 'npm downloads (30d)',\n sortStars: 'Stars',\n sortAdded: 'Release date',\n sortDesc: 'Descending',\n sortAsc: 'Ascending',\n sortNewest: 'Newest',\n sortOldest: 'Oldest',\n timeAll: 'Any time',\n timeDay: 'Last day',\n timeWeek: 'Last 7 days',\n timeMonth: 'Last 30 days',\n timeQuarter: 'Last 90 days',\n timeYear: 'Last year',\n published: 'released',\n prevPage: 'Previous',\n nextPage: 'Next',\n firstPage: 'First',\n lastPage: 'Last',\n pageInfo: 'Page {0} of {1}',\n perPage: 'Per page',\n marketUpdate: 'Update the plugin market',\n updateAll: 'Update all',\n ignoreUpdateNotice: 'Ignore for this boot',\n ignoreAllUpdateNotices: 'Ignore all for this boot',\n updateNoticeIgnored: 'Reminder ignored for this boot',\n tabThemes: 'Themes',\n tabBackup: 'Backup & Restore',\n backupLocal: 'Local file',\n backupDownload: 'Export backup',\n backupImport: 'Import and preview',\n backupHint: 'Includes the plugin list and profile configuration, never node_modules. Restore reinstalls plugins from the list.',\n webdav: 'WebDAV',\n webdavPreset: 'Provider preset',\n webdavUrl: 'Backup file URL',\n webdavUser: 'Username (optional)',\n webdavPassword: 'Password (optional)',\n webdavUpload: 'Upload backup',\n webdavRestore: 'Restore from WebDAV',\n autoBackup: 'Back up daily (when the market opens)',\n webdavNote: 'The WebDAV URL and username stay in this browser only; the password is kept server-side and must be re-entered each session.',\n localOnly: 'The WebDAV URL and credentials stay in this browser only.',\n credsWarning: 'Note: backups include profile configuration and files that may hold secrets (config.toml, .env, …). Local exports are unmodified, so upload only to a WebDAV target you trust.',\n gist: 'GitHub Gist',\n gistToken: 'GitHub token (session memory only — re-enter after refresh; leave empty to use server-side DSH_GITHUB_TOKEN or a logged-in gh CLI)',\n gistId: 'Gist ID or URL (leave empty on export to create a new private Gist)',\n gistVerify: 'Verify connection',\n gistExport: 'Export to Gist…',\n gistImport: 'Import from Gist',\n gistExportDone: 'Exported to Gist',\n gistVerifySource: 'Verified — token source: {0}',\n gistSrcToken: 'manually entered',\n gistSrcEnv: 'DSH_GITHUB_TOKEN env var',\n gistSrcGh: 'gh CLI',\n gistExportSelect: 'Select plugins to export',\n gistExportHint: 'All checked = full backup (config included); unchecking any exports only the selected plugins, with an optional “include config” flag.',\n gistSelectAll: 'Select all',\n gistSelectNone: 'Select none',\n gistIncludeConfig: 'Include config files (may contain secrets)',\n gistNoPlugins: 'This profile has no plugins to export',\n gistSpecLocal: 'local',\n gistExportGo: 'Export',\n gistCreated: 'Exported:',\n gistErrTimeout: 'GitHub request timed out — check your network and retry',\n gistErrNetwork: 'Cannot reach GitHub — check your network or proxy',\n gistErrAuth: 'GitHub authentication failed: invalid/expired token, or gh CLI not logged in',\n gistErrNotFound: 'Gist not found — check the ID or URL',\n gistErrRateLimit: 'GitHub rate limit hit, or the token lacks the gist scope',\n gistErrInvalid: 'Gist content is invalid or uses an unsupported backup format',\n gistModeUpdate: 'Update the Gist in the field',\n gistModeCreate: 'Create a new private Gist',\n gistErrNoId: 'Update mode needs a Gist ID — fill it in, or switch to “Create”',\n gistNote: \"Unlike local export (this-machine archive), Gist is for cross-machine sync: export to a private Gist, then import it on another computer with the same account to reuse your plugins. The token lives in this session's memory only and is never written to browser storage; a logged-in gh CLI or DSH_GITHUB_TOKEN skips entering it. Gists are private, 1 MB per file.\",\n backupWorking: 'In progress…',\n backupDone: 'Backup uploaded',\n restoreDone: 'Restore complete — restart DeepSeek Harness',\n restorePartial: 'Restore completed, but these plugins failed to install:',\n restoreUnportable: 'points at an absolute path from another machine that does not exist here — repoint it locally or reinstall the plugin',\n restoreBootError: 'Still cannot boot after the restore:',\n orphanBundle: '⚠️ The next start will fail: the profile declares these bundles but they are not installed. Reinstall them, or remove them from dsh.profile.bundles in the profile package.json:',\n restoreConfirm: 'Restore will overwrite this profile configuration and reinstall plugins. Continue?',\n restorePreviewDone: 'Backup imported. Review Installed, then start restore.',\n restoreMissing: '{0} plugins from this backup are not installed',\n restoreStart: 'Start restore',\n notInstalled: 'Not installed',\n themeApply: 'Use',\n themeActive: 'Active',\n themeDeactivate: 'Deactivate',\n themeEmpty: 'No themes in the catalog yet',\n themePreview: 'Preview',\n themePreviewLoading: 'Finding preview…',\n themePreviewMissing: 'Preview unavailable',\n themePreviewCount: '{0} previews',\n themeResultCount: '{0} themes',\n themeFullscreen: 'Browse full screen',\n themeExitFullscreen: 'Exit full screen',\n progressHint: 'First installs download and resolve dependencies — large plugins can take 1-3 minutes',\n toastReady: 'installed and live',\n toastTheme: 'is now active. Switch any time in Settings → Plugin Market → Themes',\n toastToggledOn: 'is now on',\n toastToggledOff: 'is now off; it stopped working immediately',\n gotIt: 'Dismiss',\n stateLive: 'Active',\n stateRestart: 'Installed — restart to apply',\n stateInert: 'Installed, not active',\n stateBroken: 'Installed, verification failed',\n stateDisabled: 'Disabled',\n phaseResolving: 'Resolving dependencies',\n phaseDownloading: 'Downloading',\n phaseLinking: 'Linking',\n phaseBuilding: 'Running build scripts',\n cancelling: 'Cancelling…',\n packagesDone: '{0} packages processed',\n updatedLive: '✓ Updated — live',\n partialNote: 'Cancelled — some changes were applied',\n reconciledNote: 'Removed, but pnpm reported an error while finishing up (usually a locked file). The profile is already reconciled — no retry needed.',\n actWhy: 'Why not live?',\n tabList: 'List',\n tabGroups: 'Groups',\n repoLink: 'View the repository on GitHub',\n disabledState: 'Disabled',\n noteAdd: 'Add note',\n noteEdit: 'Edit note',\n noteTheirs: 'Original',\n noteMine: 'My note',\n noteSave: 'Save',\n notePlaceholder: 'Describe it in your own words. This replaces the author description.',\n noteSeeTheirs: 'Show the author description',\n noteSeeMine: 'Back to my note',\n switchOnLabel: 'Enabled',\n enable: 'Enable',\n disable: 'Disable',\n toggleFail: 'Toggle failed',\n deprecatedBadge: 'Deprecated',\n deprecatedWarn: 'This plugin is marked as deprecated by the catalog; new users are advised against installing it.',\n viewReplacement: 'View replacement',\n installReplacement: 'Install replacement',\n replacementHint: 'Catalog suggests',\n groupNew: 'New group',\n groupNamePh: 'Group name',\n groupRename: 'Rename',\n groupDelete: 'Delete group',\n groupConfirmDelete: 'Delete group?',\n ungrouped: 'Ungrouped',\n groupAssign: 'Assign',\n groupRemove: 'Remove',\n groupEmpty: 'No members yet',\n groupMixed: 'Partially enabled',\n noGroups: 'No groups yet. Create one to begin.',\n groupCreate: 'Create',\n groupAdd: 'Add plugin',\n groupAddTheme: 'Add theme',\n groupAddEmpty: 'Every installed plugin is already in this group',\n tabDiagnostics: 'Diagnostics',\n checkIssues: 'Issues found',\n checkErrors: 'Errors',\n checkWarnings: 'Warnings',\n checkLoading: 'Analyzing the profile…',\n checkLoadFail: 'Failed to load diagnostics: ',\n checkProfile: 'profile',\n checkErrorsEmpty: 'No errors',\n checkWarningsEmpty: 'No warnings',\n checkBundles: 'Plugin load order (bundles)',\n checkBundlesEmpty: 'No plugin layers declared',\n checkOfficial: 'official',\n checkCommunity: 'community',\n checkSource: 'source',\n checkEntries: 'entries (loader ids)',\n checkPatch: 'patch file',\n checkDir: 'directory',\n checkDuplicates: 'Duplicate plugin entries',\n checkDuplicatesEmpty: 'No duplicate plugin entries',\n checkHoisted: 'hoisted',\n checkPeerMismatches: 'Dependency version mismatches',\n checkPeerEmpty: 'No dependency version mismatches',\n checkPeerInfo: '{0} informational entries (unconfirmed)',\n checkPeerOverview: '{0} mismatch(es) · {1} informational',\n checkRange: 'declared range',\n checkResolved: 'resolved version',\n checkSatisfied: 'satisfied',\n checkUnsatisfied: 'not satisfied',\n checkUnknown: 'unknown',\n checkPeerRisk: 'Dependency risks',\n checkPeerRiskEmpty: 'No risk-level dependency mismatches',\n checkPeerWarning: 'Dependency warnings',\n checkPeerWarningEmpty: 'No warning-level dependency mismatches',\n checkPeerInfoTier: 'Dependency info',\n checkPeerInfoTierEmpty: 'No informational dependency entries',\n checkPeerSatisfied: 'Satisfied dependencies',\n checkPeerSatisfiedEmpty: 'No satisfied dependency entries',\n peerRiskBelowMin: 'below declared minimum',\n peerRiskAboveMax: 'above explicit maximum',\n peerWarnOptional: 'optional peer',\n peerWarnAboveMax: 'above declared upper bound',\n peerInfoUnverified: 'unverified',\n checkMultiVersion: 'Multi-version core packages',\n checkMultiEmpty: 'No multi-version core packages',\n checkOverrides: 'Overrides',\n checkOverridesEmpty: 'No overrides',\n checkOverridden: 'overrides',\n checkOrphans: 'Invalid config entries',\n checkOrphansEmpty: 'No invalid config entries',\n // Orphan patch reason → badge label (issue #98 supplement).\n orphanInsertNotArray: 'malformed',\n orphanInsertTargetMissing: 'target not found',\n orphanInsertTargetNotGroup: 'target is not a group',\n orphanIdRequired: 'missing id',\n orphanPatchTargetMissing: 'target not found',\n orphanNameMismatch: 'name mismatch',\n orphanReasonOther: 'other',\n // Plain-language explainer for the diagnostics page (issue #98 UX).\n diagExplain: 'About this page',\n diagExplainText: 'This page checks for plugins that conflict with each other, mismatched dependencies, or wrong load order. Terms used:',\n diagTermBundle: 'bundle = one plugin package, applied in order',\n diagTermEntry: 'entry = one item a plugin registers in the running composition',\n diagTermPeer: 'peer dependency = another package a plugin requires from the host',\n diagTermShadow: 'shadowing = a plugin\\u2019s old copy covers the host\\u2019s newer one',\n diagTermOrphan: 'invalid entry = config references something that does not exist',\n diagTermOrder: 'load order = the order plugins activate; the later one may override the earlier',\n // issue #98 phase 2 (step 1): minimal community-bundle ordering\n orderSection: 'Ordering',\n orderUp: '↑',\n orderDown: '↓',\n orderApply: 'Apply order',\n orderDrag: 'Drag to reorder',\n orderDragHint: 'Drag ⠿ to reorder. Changes are saved when you apply the order.',\n orderConflicts: 'before/after conflicts in the current order',\n orderApplied: '✓ Applied — restart to apply',\n orderSuggestApply: 'Apply suggested order',\n orderSuggestHint: 'Suggested order (auto-sorted by before/after rules):',\n orderAutoSort: 'Auto-sort',\n orderAlreadyOptimal: 'Current order already satisfies every rule and plugin dependency — nothing to reorder',\n orderTrialFail: 'Static composition validation failed: {0}',\n orderDiffHint: 'This order would change the composition: {0} override(s), {1} orphan(s), {2} duplicate(s)',\n duplicateNames: 'Same-name plugins (informational only, not a conflict)',\n orderReset: 'Reset draft',\n checkRefresh: 'Re-check',\n aiFix: 'AI fix',\n aiFixHint: 'Hand the diagnostics to a new Agent session (copied to the clipboard; you decide whether to send)',\n aiFixIntro: 'Please help me fix the plugin issues of DeepSeek Harness (profile: {0}). Diagnostics found:',\n aiFixDetect: 'Step 1 — identify yourself first. Before touching anything, determine whether the process you are running in IS the DeepSeek Harness instance this profile belongs to (i.e. this diagnosed composition is the one you are running in right now). Check whether the cwd / $DSH_HOME / command line points at this profile, or whether you are running inside a deepseek-harness process. If you cannot tell, assume the answer is \"yes\" (the safe choice).',\n aiFixIfSelf: 'If YES — you are this harness: never change the live composition, the harness or core packages in place (don\\'t edit cordis.patch.yml, don\\'t reorder/enable/disable bundles, don\\'t install/remove/upgrade a plugin, don\\'t reinstall deps, don\\'t upgrade/restart dsh). Three steps: 1) read-only analysis only, change nothing; 2) produce apply.sh/.bat (this fix\\'s changes) and rollback.sh/.bat (fully reverts it); both are idempotent — apply snapshots only when none exists yet and never overwrites an existing snapshot (an existing one means apply already ran, so roll back or confirm before re-running), while rollback restores from that snapshot, so a half-run apply can still be rolled back and repeated rollback is safe; 3) have me run apply in an external terminal and paste the stdout/stderr back to you. State the reason and wait for my confirmation at each step — never run these yourself.',\n aiFixScope: 'If NO — you are a process outside the target harness: you may then consider reordering dsh.profile.bundles, disabling / enabling plugins, or adjusting cordis.patch.yml. Note: official bundles are fixed; state your plan before changing anything.',\n aiFixConservative: 'Be conservative: only fix the clear errors listed above (boot failures / duplicate entries / risk-level dependency mismatches). Treat warnings and informational entries (e.g. optional or unconfirmed peer ranges) only when they relate to a clear error. Do not perform unnecessary upgrades or reordering; explain each change and wait for confirmation.',\n aiFixCopied: 'Fix prompt copied — paste it into a new conversation and send when ready',\n aiFixFail: 'Clipboard unavailable — copy the diagnostics below manually',\n catConflict: 'Conflicts',\n catDeps: 'Dependencies',\n catRisk: 'Risk',\n catWarn: 'Warning',\n catInfo: 'Info',\n catOrder: 'Order',\n /** Summary-strip tooltip for the order count (before/after rule conflicts). */\n checkOrderTip: 'community bundle load order conflicts with declared before/after rules',\n diagOkAll: 'No conflict, dependency or ordering issues found',\n // issue #98 phase 3: snapshots & rollback panel\n presetSection: 'Plugin presets',\n presetHint: 'Save the current bundle order and disabled list as a preset and switch anytime; applying runs a trial-start check and creates a snapshot first.',\n presetName: 'Preset name',\n presetSave: 'Save current state',\n presetSaving: 'Saving…',\n presetSaved: 'Preset saved',\n presetApply: 'Apply',\n presetApplied: 'Applied — restart to apply',\n presetDelete: 'Delete',\n presetDeleted: 'Preset deleted',\n presetEmpty: 'No presets saved yet',\n presetNameEmpty: 'Enter a preset name',\n presetFail: 'Preset operation failed: ',\n presetListFail: 'Failed to load presets: ',\n presetBundleCount: '{0} bundles',\n presetPreview: 'Preview',\n presetPreviewTitle: 'Applying this preset will:',\n presetReorder: 'reorder {0} bundles',\n presetEnable: 'enable {0} plugins',\n presetDisable: 'disable {0} plugins',\n presetNoop: 'no changes',\n presetPreviewFail: 'Preview failed: ',\n snapSection: 'Snapshots & Rollback',\n snapHint: 'Snapshots are taken automatically before applying changes; you can also create one manually and roll back anytime.',\n snapCreate: 'Create snapshot',\n snapCreating: 'Creating…',\n snapCreated: 'Snapshot created',\n snapEmpty: 'No snapshots yet',\n snapListFail: 'Failed to load snapshots: ',\n snapCreateFail: 'Failed to create snapshot: ',\n snapRestore: 'Restore',\n snapRestoring: 'Restoring…',\n snapRestored: 'Restored — restart to apply',\n snapRestoreFail: 'Failed to restore: ',\n snapRestoreConfirm: 'Confirm restore',\n snapRestoreConfirmText: 'Restoring overwrites the current profile package.json and configuration from the snapshot; this cannot be undone (create a fresh snapshot first if unsure).',\n snapFiles: 'files',\n snapDelete: 'Delete',\n snapDeleting: 'Deleting…',\n snapDeleted: 'Snapshot deleted',\n snapDeleteFail: 'Failed to delete snapshot: ',\n snapDeleteConfirm: 'Confirm delete',\n snapDeleteConfirmText: 'This snapshot cannot be recovered after deletion.',\n marketNoToggle: 'The market itself cannot be disabled',\n hostDependencyWarning: 'A plugin lists known shared DSH host packages in dependencies. This may shadow the host version; the check is manifest-only and does not confirm a duplicate runtime instance:',\n hostDependencyMore: '{0} more finding(s) omitted',\n}\n","/**\n * Response shapes of the /dsh-market/* host routes plus the pure helpers the\n * Market UI shares between its section and toast components.\n */\n\nimport type { DiagnosticReportV1 } from '../diagnostics.ts'\nexport type { SharedHostPackageDependencyFinding } from '../diagnostics.ts'\n\n/** Localized text keyed by language ('zh' / 'en'). */\nexport type LocalizedText = Record\n\n/** One registry entry from /dsh-market/registry. */\n/**\n * Resolve a market API path against the page the UI is served from.\n *\n * Every call used to be root-absolute (`/dsh-market/…`), which the browser\n * resolves against the ORIGIN — so behind a reverse proxy that mounts dsh\n * under a prefix (`https://host/app/my-dsh/`), the panel rendered and then\n * every request in it went to `https://host/dsh-market/…`, missed the prefix\n * rule entirely, and 404'd (#345).\n *\n * Anchored on `document.baseURI`, which is the directory the host serves its\n * UI from. Safe for root deployments because that directory is `/` there, and\n * safe generally because the dsh web UI does not use path routing — measured\n * against a real dsh: `location.pathname` is `/` on the market page, not\n * `/settings/...`, so the directory really is the mount point rather than\n * wherever the user happens to have navigated.\n */\nexport function api(path: string): string {\n const relative = path.replace(/^\\/+/, '')\n if (typeof document === 'undefined') return `/${relative}`\n return new URL(relative, document.baseURI).pathname\n}\n\nexport interface RegistryPlugin {\n name: string\n owner: string\n url: string\n npm?: string\n tarball?: string | null\n /** One legacy category id or several category ids. */\n category: string | string[]\n description?: LocalizedText\n stars?: number\n /**\n * npm downloads in the last 30 days, when the entry has a published\n * package. Absent means \"no npm package\" — a coverage gap, not a zero.\n */\n downloads?: number | null\n added?: string\n install?: string\n /**\n * Catalog-side deprecation flags (#60): absent for every normal entry, so\n * catalogs without the fields render exactly as before.\n */\n deprecated?: boolean\n /** Catalog name of the suggested replacement plugin, when deprecated. */\n replacement?: string\n /** Author-curated screenshot URLs from the registry (#61); optional. */\n screenshots?: string[]\n}\n\n/** Category ids for one entry, de-duplicated in declaration order. */\nexport function pluginCategories(plugin: Pick): string[] {\n const values: unknown[] = Array.isArray(plugin.category) ? plugin.category : [plugin.category]\n const categories: string[] = []\n const seen = new Set()\n for (const value of values) {\n if (typeof value !== 'string' || value === '' || seen.has(value)) continue\n seen.add(value)\n categories.push(value)\n }\n return categories\n}\n\n/** The catalog payload under `registry` in /dsh-market/registry. */\nexport interface Registry {\n count: number\n categories: Record\n plugins: RegistryPlugin[]\n}\n\n/** Profile dependency map: package name → install spec. */\nexport type InstalledMap = Record\n\n/**\n * Add active profile Bundles as presence-only catalog entries.\n *\n * The returned map is for catalog matching only. Update and uninstall flows\n * must keep using the dependency-only map because a Bundle supplied by the\n * dsh installation is not owned by the profile package manager.\n */\nexport function installedForCatalog(installed: InstalledMap, bundles: readonly string[]): InstalledMap {\n return Object.fromEntries([\n ...bundles.map(name => [name, '*'] as const),\n ...Object.entries(installed),\n ])\n}\n\n/** Strong repo identities discovered for local link:/file: dependencies (#141). */\nexport type InstalledRepoIdentities = Record\n\n/** Weak Git-origin hints used only to disambiguate multiple same-named entries. */\nexport type InstalledRepoHints = Record\n\n/** Response of the /dsh-market/gist export action. */\nexport interface GistExportResult {\n ok: boolean\n gistId: string\n gistUrl: string\n}\n\n/** Per-package update status from /dsh-market/updates. */\nexport interface UpdateStatus {\n updateAvailable?: boolean\n version?: string\n kind?: string\n /** What is installed and what the source of truth offers — versions for npm\n packages, commit shas for github installs; the notes dialog (#294) shows\n the range between them in whichever form reads best. */\n current?: string | null\n latest?: string | null\n /** Updating this local package switches it to its matched online release. */\n restoreRequired?: boolean\n}\n\n/** Poll payload from /dsh-market/status. */\nexport interface MarketStatus {\n /** The market's own version — rendered in the heading so screenshots carry it. */\n version?: string\n /** Whether the profile package manager owns the market dependency. */\n selfManaged?: boolean\n /**\n * Prefix to put in front of github.com URLs the BROWSER loads, or null to\n * address them directly. Resolved by the server from the download region.\n */\n githubProxy?: string | null\n active?: boolean\n lastLine?: string\n seconds?: number\n installed?: InstalledMap\n pnpm?: boolean\n boot?: string\n /** pnpm ndjson stage, when the structured reporter produced events. */\n phase?: 'resolving' | 'downloading' | 'linking' | 'building' | null\n done?: number\n total?: number | null\n currentPackage?: string | null\n downloaded?: number | null\n size?: number | null\n /** True once the user asked to cancel and the host is killing the run. */\n cancelling?: boolean\n /**\n * The route-level operation lock (#91): stays true through install\n * post-processing after pnpm already exited (progress.active false).\n * Restart must not be offered while it is held.\n */\n busy?: boolean\n /**\n * The process supervisor the host detected around itself (systemd, pm2),\n * or null/absent when none. Present so the UI can explain WHY the restart\n * button is missing instead of just omitting it (#229).\n */\n supervisor?: string | null\n /**\n * Debugger latch (#447): `'inspector'` when the host is under a debugger,\n * or null/absent otherwise. Kept separate from `supervisor` and from\n * `restart` so `allowRestart` settings are not conflated with debug state.\n */\n debugger?: string | null\n}\n\n/** Post-install activation state (P0-2), per installed package. */\nexport type ActivationState = 'live' | 'restart' | 'inert' | 'broken' | 'missing' | 'disabled'\n\nexport interface ActivationInfo {\n state: ActivationState\n reasons: string[]\n bundle: boolean\n hot: boolean\n}\n\n/** The /dsh-market/installed payload (fields the market UI consumes). */\nexport interface InstalledPayload {\n profile?: string\n installed: InstalledMap\n /** Strong source identities for local link:/file: dependencies (#141). */\n repoIdentities?: InstalledRepoIdentities\n /** Weak local Git-origin hints; never used to reject a unique match. */\n repoHints?: InstalledRepoHints\n activation?: Record\n diagnostics?: DiagnosticReportV1\n live?: string[]\n /** Plugins the user switched off; persisted across restarts (#60). */\n disabled?: string[]\n /**\n * Packages whose bundle rows the user patch layer (cordis.patch.yml)\n * disables / force-enables (port of dsh-plugin-hub). Covers toggles made\n * OUTSIDE the market — hand-edited patch files, the dsh CLI — which the\n * market's own disable list never sees.\n */\n patchDisabled?: string[]\n patchForced?: string[]\n /** Custom plugin groups: group name → member package names. */\n groups?: Record\n /** Display order of group names. */\n groupOrder?: string[]\n}\n\n/**\n * A group's derived switch state: all members enabled / all disabled /\n * mixed / no members. Pure — the UI renders exactly this and the group\n * switch itself is never persisted (#60).\n */\nexport type GroupSwitchState = 'on' | 'off' | 'mixed' | 'empty'\n\nexport function groupSwitchState(members: string[] | undefined, disabled: ReadonlySet): GroupSwitchState {\n const list = members ?? []\n if (list.length === 0) return 'empty'\n let anyOn = false\n let anyOff = false\n for (const member of list) {\n if (disabled.has(member)) anyOff = true\n else anyOn = true\n }\n return anyOn && anyOff ? 'mixed' : anyOff ? 'off' : 'on'\n}\n\n/** Registered theme definition surfaced by the theme service snapshot. */\nexport interface ThemeDef {\n id: string\n colorScheme?: string\n tokens?: Record\n}\n\n/** Theme service snapshot; null when the composition has no theme service. */\nexport interface ThemeSnapshot {\n preference: string\n themes: ThemeDef[]\n}\n\n/** Bound locale translator for the dsh-market namespace. */\nexport type Translate = (key: string) => string\n\nexport function avatarColor(name: string): string {\n let hash = 0\n for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) | 0\n return 'hsl(' + (((hash % 360) + 360) % 360) + ' 55% 52%)'\n}\n\nexport function readSession(key: string): any {\n try { return JSON.parse(sessionStorage.getItem(key) || 'null') } catch { return null }\n}\n\n/** Heuristic: plugins that target a terminal surface rather than the web UI. */\nexport function looksTerminal(plugin: RegistryPlugin, lang: string): boolean {\n const desc = (plugin.description && (plugin.description[lang] || plugin.description.en)) || ''\n // A description can mention a CLI only to say it is NOT required. Treating\n // that as positive evidence labels web plugins as terminal-only. Strip\n // bounded negated clauses before applying the deliberately broad heuristic;\n // the package name remains untouched and therefore stays strong evidence.\n const positiveDesc = desc\n .replace(/\\b(?:no|without)\\b[^.!?;:,。!?;\\n]{0,80}\\b(?:tui|cli|tty|terminal)\\b/gi, '')\n .replace(/(?:无需|无须|不需要|不用)[^。!?;\\n]{0,48}(?:tui|cli|tty|terminal|终端|命令行)/gi, '')\n return /\\b(tui|cli|tty|terminal)\\b|终端|命令行/i.test(plugin.name + ' ' + positiveDesc)\n}\n\n/** Sortable field for the Discover list. */\nexport type SortField = 'downloads' | 'stars' | 'added'\n/** Sort direction: desc = newest/most first, asc = oldest/least first. */\nexport type SortDir = 'desc' | 'asc'\n/** Combined sort key sent to visiblePlugins. */\nexport type SortKey = `${SortField}-${SortDir}`\n\n/** Recency windows for the \"published within\" filter. */\nexport type TimeRange = 'all' | 'day' | 'week' | 'month' | 'quarter' | 'year'\n\n/** Days per TimeRange (`all` has no cutoff and is handled by the caller). */\nexport const TIME_RANGE_DAYS: Record, number> = {\n day: 1,\n week: 7,\n month: 30,\n quarter: 90,\n year: 365,\n}\n\n/** True when `added` is a date within the last `days` days (inclusive). */\nexport function withinDays(added: string | undefined, days: number): boolean {\n if (added === undefined || added === '') return false\n const time = Date.parse(added)\n if (Number.isNaN(time)) return false\n const age = Date.now() - time\n return age >= 0 && age <= days * 86_400_000\n}\n\n/** Filters and sort order driving the discover list. */\nexport interface ListQuery {\n /** Active category id, or 'all'. */\n category: string\n /** Raw search input (trimmed and lowercased internally). */\n query: string\n /** UI language for description matching ('zh' / 'en'). */\n lang: string\n /** Category labels indexed by id; omitted by callers that do not need label search. */\n categories?: Record\n /** 'stars-desc' | 'stars-asc' | 'added-desc' | 'added-asc'; anything else keeps registry order. */\n sort: string\n /** Keep only plugins published within the last N days; undefined = any time. */\n sinceDays?: number\n}\n\n/**\n * Whether a catalog entry IS the market itself. The catalog still carries\n * it — nothing about the data changes, and the Installed tab still shows it\n * — this is purely \"a store has no reason to sell itself to someone already\n * standing in it.\"\n */\nexport function isMarketItself(plugin: Pick): boolean {\n return plugin.name === 'dsh-market' || plugin.npm === 'dshmarket'\n}\n\n/** Normalize punctuation-separated package names and human text alike. */\nfunction searchText(value: string): string {\n return value.normalize('NFKC').toLowerCase().replace(/[^\\p{L}\\p{N}]+/gu, ' ').trim().replace(/\\s+/g, ' ')\n}\n\n/**\n * Normalized catalog fields are immutable for the lifetime of one registry\n * entry. Keep them with that entry so typing does not repeat unicode\n * normalization across the whole catalog, while replaced catalogs remain\n * collectible. The raw query is intentionally not cached: it is normalized\n * once per call and would otherwise grow the cache on every keystroke.\n */\nconst pluginSearchTextCache = new WeakMap>()\n\nfunction cachedPluginSearchText(plugin: RegistryPlugin, value: string): string {\n let fields = pluginSearchTextCache.get(plugin)\n if (fields === undefined) {\n fields = new Map()\n pluginSearchTextCache.set(plugin, fields)\n }\n const hit = fields.get(value)\n if (hit !== undefined) return hit\n const normalized = searchText(value)\n fields.set(value, normalized)\n return normalized\n}\n\n/**\n * Relevance within one field. Exact and prefix matches beat phrase matches;\n * for a multi-word query every word must occur in the same field.\n */\nfunction fieldRelevance(\n plugin: RegistryPlugin,\n value: string | undefined,\n query: string,\n tokens: string[],\n weight: number,\n): number {\n if (!value) return 0\n const text = cachedPluginSearchText(plugin, value)\n if (text === '' || !tokens.every(token => text.includes(token))) return 0\n if (text === query) return weight + 300\n if (text.startsWith(query)) return weight + 250\n if (text.includes(query)) return weight + 200\n return weight + 150\n}\n\n/**\n * Search ranking is field-aware rather than a popularity-only filter:\n * package identities outrank owners, descriptions, and categories. The\n * selected popularity/date sort remains the tie-breaker between equally\n * relevant entries.\n */\nfunction pluginRelevance(\n plugin: RegistryPlugin,\n query: string,\n tokens: string[],\n lang: string,\n categories: Record | undefined,\n): number {\n const descriptions = plugin.description ?? {}\n const preferredLocale = descriptions[lang] ? lang : descriptions.en ? 'en' : null\n const preferredDescription = descriptions[lang] || descriptions.en\n const otherDescriptions = Object.entries(descriptions)\n .filter(([locale, value]) => locale !== preferredLocale && typeof value === 'string')\n .map(([, value]) => value)\n const categoryIds = pluginCategories(plugin)\n const categoryLabels = categoryIds.flatMap(category => Object.values(categories?.[category] ?? {}))\n\n return Math.max(\n fieldRelevance(plugin, plugin.name, query, tokens, 700),\n fieldRelevance(plugin, plugin.npm, query, tokens, 700),\n fieldRelevance(plugin, plugin.owner, query, tokens, 400),\n fieldRelevance(plugin, preferredDescription, query, tokens, 280),\n ...otherDescriptions.map(value => fieldRelevance(plugin, value, query, tokens, 240)),\n ...categoryIds.map(value => fieldRelevance(plugin, value, query, tokens, 180)),\n ...categoryLabels.map(value => fieldRelevance(plugin, value, query, tokens, 180)),\n )\n}\n\n/** Compare two already-filtered entries using the user's selected sort. */\nfunction comparePlugins(a: RegistryPlugin, b: RegistryPlugin, sort: string): number {\n // A github:-only entry has no npm package and therefore no download count\n // at all — that is a coverage gap, not a \"0 downloads\" verdict, and must\n // not be read as less popular than a package that genuinely has zero.\n // Such entries always sort after every entry WITH a real count, in either\n // direction, and are ordered against each other by star count — the only\n // signal available for them — rather than left in an arbitrary tie.\n const hasDownloads = (p: RegistryPlugin): p is RegistryPlugin & { downloads: number } => typeof p.downloads === 'number'\n if (sort === 'downloads-desc') {\n if (hasDownloads(a) && hasDownloads(b)) return b.downloads - a.downloads\n if (hasDownloads(a)) return -1\n if (hasDownloads(b)) return 1\n return (b.stars ?? -1) - (a.stars ?? -1)\n }\n if (sort === 'downloads-asc') {\n if (hasDownloads(a) && hasDownloads(b)) return a.downloads - b.downloads\n if (hasDownloads(a)) return -1\n if (hasDownloads(b)) return 1\n return (a.stars ?? -1) - (b.stars ?? -1)\n }\n if (sort === 'stars-desc') return (b.stars ?? -1) - (a.stars ?? -1)\n if (sort === 'stars-asc') return (a.stars ?? -1) - (b.stars ?? -1)\n if (sort === 'added-desc') return String(b.added).localeCompare(String(a.added))\n if (sort === 'added-asc') return String(a.added).localeCompare(String(b.added))\n return 0\n}\n\n/**\n * The discover list: category filter, then the published-within window, then\n * relevance-ranked search across package identity / owner / every localized\n * description / category ids and labels. With no search, only the selected\n * sort applies, preserving the existing discover-list behaviour.\n * Pure — the section renders exactly this.\n */\nexport function visiblePlugins(plugins: RegistryPlugin[], options: ListQuery): RegistryPlugin[] {\n const query = searchText(options.query)\n const tokens = query.split(' ').filter(Boolean)\n const scored = plugins.flatMap((plugin, index) => {\n if (isMarketItself(plugin)) return []\n const categories = pluginCategories(plugin)\n if (options.category !== 'all' && !categories.includes(options.category)) return []\n if (options.sinceDays !== undefined && !withinDays(plugin.added, options.sinceDays)) return []\n const relevance = query === '' ? 0 : pluginRelevance(plugin, query, tokens, options.lang, options.categories)\n return relevance === 0 && query !== '' ? [] : [{ plugin, relevance, index }]\n })\n\n return scored.sort((a, b) =>\n b.relevance - a.relevance\n || comparePlugins(a.plugin, b.plugin, options.sort)\n || a.index - b.index,\n ).map(row => row.plugin)\n}\n\n/** The themes tab listing: theme category only, most-starred first. */\nexport function themePlugins(plugins: RegistryPlugin[]): RegistryPlugin[] {\n return plugins.filter(p => pluginCategories(p).includes('theme')).sort((a, b) => (b.stars || 0) - (a.stars || 0))\n}\n\n/**\n * Category chip order: collapsed with an active non-'all' chip that would\n * otherwise be clipped out of the two-row preview, the active one moves to\n * the front so it stays visible.\n *\n * Reported as \"点了某个分类,标签就跑到前面来了,好奇怪\": the earlier version\n * moved the active chip to the front unconditionally, so clicking a category\n * that was ALREADY visible inside the two rows still reshuffled it — and\n * every chip after it — for no reason, since nothing was at risk of being\n * hidden. `visibleCount` is how many chips (the 'all' chip included) the\n * two-row clip fits; a category already within that budget in its natural\n * position is left exactly where it was.\n *\n * `visibleCount === null` (not yet measured, e.g. the very first collapsed\n * render) keeps the old unconditional behaviour: with no measurement to\n * check against, guaranteeing visibility is the safe default.\n */\nexport function orderedCategories(\n categories: string[],\n active: string,\n open: boolean,\n visibleCount: number | null = null,\n): string[] {\n if (open || active === 'all') return categories\n if (visibleCount !== null) {\n // One slot of the budget is always the 'all' chip itself.\n const budget = Math.max(0, visibleCount - 1)\n const naturalIndex = categories.indexOf(active)\n if (naturalIndex !== -1 && naturalIndex < budget) return categories\n }\n return [active, ...categories.filter(id => id !== active)]\n}\n\n/**\n * Page-number list for the discover pager. With few pages it is simply\n * 1..total; with many it windows around the current page and inserts '…'\n * so a 400-plugin catalog stays a compact `1 … 4 5 6 … 17` instead of a\n * long row of numbered buttons. Always begins with 1 and ends with total.\n */\nexport function pageItems(current: number, total: number): Array {\n if (total <= 7) {\n const all: number[] = []\n for (let i = 1; i <= total; i++) all.push(i)\n return all\n }\n const items: Array = [1]\n let start = Math.max(2, current - 1)\n let end = Math.min(total - 1, current + 1)\n if (current <= 4) end = 5\n if (current >= total - 3) start = total - 4\n if (start > 2) items.push('…')\n for (let i = start; i <= end; i++) items.push(i)\n if (end < total - 1) items.push('…')\n items.push(total)\n return items\n}\n\n/**\n * Unified installed-state matching (#15): both sides collapse to lowercase\n * identity sets — the registry entry contributes its bare name, npm name and\n * owner/repo; the dependency contributes its key and the repo inside its\n * spec — and any exact intersection counts. Exact equality, not substrings,\n * so prefix-related repo names cannot cross-match.\n */\n/**\n * Memo for entryIdentities, keyed on the catalog entry object itself.\n *\n * Catalog entries are parsed once and never mutated, so the identity set is\n * a pure function of an object that outlives every call — a WeakMap holds\n * it for exactly as long as the catalog is alive and not one render longer.\n * Worth caching because this is the innermost step of the installed-state\n * matching that runs for every card on screen (#262).\n */\nconst entryIdCache = new WeakMap>()\n\nfunction entryIdentities(plugin: RegistryPlugin): Set {\n const cached = entryIdCache.get(plugin)\n if (cached !== undefined) return cached\n const ids = new Set([plugin.name.toLowerCase()])\n if (plugin.npm) ids.add(plugin.npm.toLowerCase())\n // Subpath-aware: a /tree/ entry identifies as repo#path:/sub, never the\n // bare repo — two subpackages of one monorepo must not cross-match.\n const m = /^https:\\/\\/github\\.com\\/([^/]+\\/[^/]+?)(?:\\/tree\\/[^/]+\\/(.+?))?\\/?$/.exec(plugin.url)\n if (m !== null) {\n ids.add(m[2] !== undefined ? `${m[1]!.toLowerCase()}#path:/${m[2].toLowerCase()}` : m[1]!.toLowerCase())\n }\n entryIdCache.set(plugin, ids)\n return ids\n}\n\nconst REPO_ID_RE = /^[A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+(?:#path:\\/[A-Za-z0-9_./-]+)?$/\n\nfunction addRepoIdentities(ids: Set, values: readonly string[]): void {\n for (const value of values) {\n if (!REPO_ID_RE.test(value)) continue\n const subpath = value.split('#path:/')[1]\n if (subpath !== undefined && subpath.split('/').some(seg => seg === '' || seg === '.' || seg === '..')) continue\n ids.add(value.toLowerCase())\n }\n}\n\n/** Repo identities carried by a github shortcut, including `#sha&path:`. */\nfunction githubSpecRepoIds(spec: string): Set {\n const ids = new Set()\n const match = /^github:([A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+?)(?:\\.git)?(?:#(.*))?$/i.exec(spec)\n if (match === null) return ids\n const repo = match[1]!.toLowerCase()\n let subpath: string | null = null\n for (const selector of (match[2] ?? '').split('&')) {\n if (!selector.startsWith('path:/')) continue\n const candidate = selector.slice('path:/'.length)\n if (!REPO_ID_RE.test(`${repo}#path:/${candidate}`)\n || candidate.split('/').some(seg => seg === '' || seg === '.' || seg === '..')\n || subpath !== null) return new Set()\n subpath = candidate.toLowerCase()\n }\n ids.add(repo)\n if (subpath !== null) ids.add(`${repo}#path:/${subpath}`)\n return ids\n}\n\nfunction depIdentities(name: string, spec: string, repoIdentities: readonly string[] = []): Set {\n const ids = new Set([name.toLowerCase()])\n // A scoped npm key usually mirrors owner/repo — expose that identity so an\n // npm-installed plugin still matches an entry whose npm field is unset.\n const scoped = /^@([^/]+)\\/(.+)$/.exec(name)\n if (scoped !== null) ids.add(`${scoped[1]!.toLowerCase()}/${scoped[2]!.toLowerCase()}`)\n for (const id of githubSpecRepoIds(spec)) ids.add(id)\n addRepoIdentities(ids, repoIdentities)\n return ids\n}\n\n/**\n * Repo identities stated by the dependency SPEC itself (github: installs) —\n * hard evidence of where the package came from, unlike the name-derived\n * mirror in depIdentities, which is only a matching aid.\n */\nfunction depRepoIds(spec: string, repoIdentities: readonly string[] = []): Set {\n const ids = githubSpecRepoIds(spec)\n addRepoIdentities(ids, repoIdentities)\n return ids\n}\n\n/** Repo identity of a registry entry's source url (repo or repo#path form). */\nfunction entryRepoIds(plugin: RegistryPlugin): Set {\n const ids = new Set()\n const m = /^https:\\/\\/github\\.com\\/([^/]+\\/[^/]+?)(?:\\/tree\\/[^/]+\\/(.+?))?\\/?$/.exec(plugin.url)\n if (m !== null) {\n ids.add(m[2] !== undefined ? `${m[1]!.toLowerCase()}#path:/${m[2].toLowerCase()}` : m[1]!.toLowerCase())\n }\n return ids\n}\n\n/**\n * The curated registry lists distinct plugins sharing one name — twelve\n * name-groups at the time of #66 (both dsh-usage-stats, four dsh-memory…).\n * A name coincidence must not survive contradicting repo evidence: when the\n * dependency's spec pins a github repo AND the entry states one, the repos\n * decide — the loose name/npm identities only apply when at least one side\n * carries no repo evidence (npm installs, non-github entries).\n */\nfunction sameSourceConflict(plugin: RegistryPlugin, spec: string, repoIdentities: readonly string[] = []): boolean {\n const entry = entryRepoIds(plugin)\n const dep = depRepoIds(spec, repoIdentities)\n if (entry.size === 0 || dep.size === 0) return false\n for (const id of dep) if (entry.has(id)) return false\n return true\n}\n\nfunction repoHintMatches(plugin: RegistryPlugin, hints: readonly string[]): boolean {\n const entry = entryRepoIds(plugin)\n const values = new Set()\n addRepoIdentities(values, hints)\n for (const id of values) if (entry.has(id)) return true\n return false\n}\n\n/**\n * Memo for looseMatchCount, keyed on the catalog array then the dep name.\n *\n * This is THE hot path behind \"the plugin list is very laggy\" (#262). The\n * count answers \"how many catalog entries could this installed dependency\n * be?\", which depends only on the catalog and the name — not on the card\n * being drawn. But it was called from matchInstalledName, which runs once\n * per installed dependency, which runs once per rendered card: a full scan\n * of ~1800 entries, repeated cards × installed times, on every single\n * render. A profile from the reporter put it at 2.9 seconds, 28% of the\n * whole trace, and a local benchmark measured 48ms per render at 24 cards\n * and 224ms at 96 against a smaller 839-entry catalog.\n *\n * Keyed on the array identity so a refetched catalog gets a fresh map for\n * free — a new parse is a new array, and the old one is collectable.\n */\nconst looseMatchCountCache = new WeakMap>()\n\nfunction looseMatchCount(plugins: RegistryPlugin[], name: string): number {\n let byName = looseMatchCountCache.get(plugins)\n if (byName === undefined) {\n byName = new Map()\n looseMatchCountCache.set(plugins, byName)\n }\n const hit = byName.get(name)\n if (hit !== undefined) return hit\n // Built once for the whole scan. looseMatches() rebuilt this identity set\n // for every entry it tested, so the allocation alone ran ~1800 times per\n // call before this.\n const dep = depIdentities(name, '')\n let count = 0\n for (const plugin of plugins) {\n for (const id of entryIdentities(plugin)) {\n if (dep.has(id)) { count += 1; break }\n }\n }\n byName.set(name, count)\n return count\n}\n\nfunction looseMatches(plugin: RegistryPlugin, name: string): boolean {\n const dep = depIdentities(name, '')\n for (const id of entryIdentities(plugin)) if (dep.has(id)) return true\n return false\n}\n\n/** The installed dependency name a registry entry corresponds to, or null. */\nexport function matchInstalledName(\n plugin: RegistryPlugin,\n installed: InstalledMap,\n repoIdentities: InstalledRepoIdentities = {},\n plugins?: RegistryPlugin[],\n repoHints: InstalledRepoHints = {},\n): string | null {\n const ids = entryIdentities(plugin)\n for (const [name, spec] of Object.entries(installed)) {\n const repos = repoIdentities[name] ?? []\n if (depRepoIds(String(spec), repos).size === 0 && plugins !== undefined && looseMatchCount(plugins, name) > 1\n && !repoHintMatches(plugin, repoHints[name] ?? [])) continue\n if (sameSourceConflict(plugin, String(spec), repos)) continue\n for (const id of depIdentities(name, String(spec), repos)) {\n if (ids.has(id)) return name\n }\n }\n return null\n}\n\n/** The registry entry an installed dependency corresponds to, or undefined. */\nexport function entryForDep(\n plugins: RegistryPlugin[],\n name: string,\n spec: string,\n repoIdentities: readonly string[] = [],\n repoHints: readonly string[] = [],\n): RegistryPlugin | undefined {\n if (depRepoIds(String(spec), repoIdentities).size === 0 && looseMatchCount(plugins, name) > 1) {\n const hinted = plugins.find(plugin => repoHintMatches(plugin, repoHints) && looseMatches(plugin, name))\n if (hinted === undefined) return undefined\n }\n const ids = depIdentities(name, String(spec), repoIdentities)\n return plugins.find((plugin) => {\n if (sameSourceConflict(plugin, String(spec), repoIdentities)) return false\n for (const id of entryIdentities(plugin)) if (ids.has(id)) return true\n return false\n })\n}\n\nexport function isInstalled(\n plugin: RegistryPlugin,\n installed: InstalledMap,\n repoIdentities: InstalledRepoIdentities = {},\n plugins?: RegistryPlugin[],\n repoHints: InstalledRepoHints = {},\n): boolean {\n return matchInstalledName(plugin, installed, repoIdentities, plugins, repoHints) !== null\n}\n\n/**\n * The header brand mark now lives in MarketSection.tsx as an inline SVG\n * (official-style monochrome glyph, fill=\"currentColor\") so it follows the\n * active theme; the colored assets/logo.svg tile is no longer inlined here.\n */\n\n/** Four representative colors for a theme card's preview strip. */\nexport function themeSwatch(def: ThemeDef): string[] {\n const tk = def.tokens || {}\n const pick = (names: string[]) => { for (const n of names) { if (tk[n]) return tk[n]! } return null }\n const dark = def.colorScheme === 'dark'\n return [\n pick(['--dsw-alias-bg-base', '--dsw-alias-bg-layer-1']) || (dark ? '#0f1115' : '#ffffff'),\n pick(['--dsw-alias-bg-layer-2', '--dsw-alias-bg-overlay']) || (dark ? '#1a1d23' : '#f3f4f6'),\n pick(['--dsw-alias-brand-primary']) || '#4f6ef7',\n pick(['--dsw-alias-label-primary']) || (dark ? '#e5e7eb' : '#1f2328'),\n ]\n}\n\n// ------------------------------------------------------------- screenshots\n\n/**\n * Prefix for github.com URLs this page loads, or null to address them\n * directly. Set from the status poll, which gets it from the download region.\n *\n * Module state rather than a prop: the URLs it applies to are built in four\n * places across two files (avatars, README fetches, screenshot thumbnails),\n * and threading one string through every card would put it in signatures\n * that have no other reason to know about networking.\n *\n * Applied at the LAST moment, never stored. Extracted image URLs stay\n * canonical, so changing region re-renders against the new route instead of\n * leaving a page full of links to a proxy the user just switched away from.\n */\nlet githubProxy: string | null = null\n\n/** Point browser-side github.com requests at a proxy, or null for direct. */\nexport function setGithubProxy(proxy: string | null): void {\n githubProxy = proxy\n}\n\n/** The proxy in force, for callers that must decide between two URL shapes. */\nexport function githubProxyInUse(): string | null {\n return githubProxy\n}\n\n/** `url` through the proxy in force, or unchanged when there is none. */\nexport function githubUrl(url: string): string {\n return githubProxy === null ? url : `${githubProxy}/${url}`\n}\n\n/**\n * Image hosts screenshots may load from (#61) — GitHub's own hosting only.\n * Any other host is dropped BEFORE an is created: a screenshot URL is\n * a request carrying the user's IP, so registry data and README content are\n * both treated as untrusted here, matching the upstream build gate.\n */\nconst SCREENSHOT_HOSTS = new Set([\n 'raw.githubusercontent.com',\n 'user-images.githubusercontent.com',\n 'camo.githubusercontent.com',\n 'github.com',\n])\n\nconst MAX_SCREENSHOTS = 6\n\n/** A README image together with the evidence used to rank it as a preview. */\nexport interface ScreenshotCandidate {\n src: string\n semanticScore: number\n order: number\n curated: boolean\n}\n\n/** Dimensions observed from a low-resolution, no-upscale image probe. */\nexport interface ScreenshotMeasurement {\n src: string\n width: number\n height: number\n}\n\n/** Return one safe screenshot URL without applying the public list limit. */\nfunction safeScreenshot(value: unknown): string | null {\n if (typeof value !== 'string') return null\n let parsed: URL\n try { parsed = new URL(value) } catch { return null }\n if (parsed.protocol !== 'https:' || !SCREENSHOT_HOSTS.has(parsed.hostname)) return null\n if (/\\.svg$/iu.test(parsed.pathname)) return null\n return value\n}\n\n/** Keep only https URLs on allowlisted image hosts; SVG dropped (logos/badges). */\nexport function safeScreenshots(urls: unknown): string[] {\n if (!Array.isArray(urls)) return []\n const safe: string[] = []\n for (const value of urls) {\n const src = safeScreenshot(value)\n if (src === null) continue\n if (!safe.includes(src)) safe.push(src)\n if (safe.length >= MAX_SCREENSHOTS) break\n }\n return safe\n}\n\nconst PREVIEW_WORDS = /(?:preview|screen[ -]?shots?|shots?|demo|showcase|gallery|theme|skin|appearance|效果|预览|截图|演示|展示|界面|主题|皮肤)/iu\nconst FULL_PREVIEW_WORDS = /(?:full|overview|home|main|conversation|chat|workspace|dashboard|完整|主页|首页|全景|主界面)/iu\nconst PARTIAL_PREVIEW_WORDS = /(?:settings?|panel|dialog|modal|picker|menu|controls?|fragment|crop|detail|配置|设置|面板|弹窗|局部|细节)/iu\nconst NON_PREVIEW_WORDS = /(?:badge|shield|logo|icon|avatar|sponsor|donat|fund|qr(?:code)?|wechat|qq(?:group)?|npm|build|coverage|license|status|button|favicon|徽章|图标|头像|赞助|捐赠|二维码|微信|交流群)/iu\n\ninterface ReadmeImageParts {\n src: string\n alt: string\n title: string\n width: number | null\n height: number | null\n}\n\n/** A quoted or unquoted HTML attribute; README HTML is data, never rendered. */\nfunction htmlAttribute(html: string, name: string): string {\n const match = new RegExp(`\\\\b${name}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s>]+))`, 'iu').exec(html)\n return match?.[1] ?? match?.[2] ?? match?.[3] ?? ''\n}\n\nfunction numericDimension(raw: string): number | null {\n if (!/^\\d+(?:\\.\\d+)?$/u.test(raw.trim())) return null\n const value = Number(raw)\n return Number.isFinite(value) && value > 0 ? value : null\n}\n\n/** Resolve one README image path to the canonical GitHub-hosted URL. */\nfunction resolveReadmeImage(raw: string, owner: string, repo: string, base: string): string | null {\n const src = raw.trim().replace(/^<|>$/g, '')\n if (src === '' || src.startsWith('data:')) return null\n let absolute: string\n if (/^https?:\\/\\//iu.test(src)) {\n absolute = src\n } else if (src.startsWith('/')) {\n absolute = `https://raw.githubusercontent.com/${owner}/${repo}/HEAD${src}`\n } else {\n try { absolute = new URL(src, base).href } catch { return null }\n }\n return safeScreenshot(absolute)\n}\n\n/** Score evidence available without downloading the image itself. */\nfunction readmeSemanticScore(\n image: ReadmeImageParts,\n heading: string,\n nearby: string,\n order: number,\n offset: number,\n): number {\n const label = `${image.alt} ${image.title}`\n const path = (() => {\n try {\n const parsed = new URL(image.src)\n // Do not count owner/repository names as image evidence: practically\n // every entry here contains \"theme\" or \"skin\" in its repo name.\n if (parsed.hostname === 'raw.githubusercontent.com') {\n return '/' + parsed.pathname.split('/').slice(4).join('/')\n }\n return parsed.pathname\n } catch { return image.src }\n })()\n let score = 20 + Math.max(0, 8 - order)\n if (PREVIEW_WORDS.test(label)) score += 55\n if (PREVIEW_WORDS.test(path)) score += 40\n if (PREVIEW_WORDS.test(heading)) score += 32\n if (PREVIEW_WORDS.test(nearby)) score += 12\n if (FULL_PREVIEW_WORDS.test(`${label} ${path}`)) score += 35\n if (PARTIAL_PREVIEW_WORDS.test(`${label} ${path}`)) score -= 30\n if (NON_PREVIEW_WORDS.test(label)) score -= 140\n if (NON_PREVIEW_WORDS.test(path)) score -= 120\n if (NON_PREVIEW_WORDS.test(heading)) score -= 55\n if (NON_PREVIEW_WORDS.test(nearby)) score -= 18\n // A title-block image with no screenshot evidence is usually branding.\n if (offset < 500 && !PREVIEW_WORDS.test(`${label} ${path} ${heading}`)) score -= 20\n if (image.width !== null && image.height !== null) {\n score += previewDimensionScore(image.width, image.height) ?? -500\n } else if ((image.width ?? image.height ?? Number.POSITIVE_INFINITY) < 240) {\n score -= 100\n }\n return score\n}\n\n/**\n * Ranked README image candidates for use when the catalog has no curated\n * screenshots. Ranking uses the image label/path, nearest heading, nearby\n * prose, declared dimensions and document position. This prevents a title\n * logo or a row of tiny badges from consuming the six-candidate limit before\n * a later Screenshots section is reached.\n */\nexport function extractReadmeImageCandidates(\n markdown: string,\n owner: string,\n repo: string,\n subpath: string | null,\n): ScreenshotCandidate[] {\n const base = `https://raw.githubusercontent.com/${owner}/${repo}/HEAD/${subpath === null ? '' : subpath + '/'}`\n const headings = [...markdown.matchAll(/^#{1,6}\\s+(.+?)\\s*#*\\s*$/gmu)]\n .map(match => ({ offset: match.index, text: match[1] ?? '' }))\n const found = new Map()\n let headingIndex = -1\n let order = 0\n // Markdown and HTML image forms stay in one pass, preserving position.\n const imagePattern = /!\\[([^\\]]*)\\]\\(\\s*(?:<([^>]+)>|([^\\s)]+))(?:\\s+(?:\"([^\"]*)\"|'([^']*)'|\\(([^)]*)\\)))?\\s*\\)|]*?)\\/?\\s*>/gimu\n for (const match of markdown.matchAll(imagePattern)) {\n while (headingIndex + 1 < headings.length && headings[headingIndex + 1]!.offset < match.index) headingIndex += 1\n const html = match[7] ?? ''\n const rawSrc = match[2] ?? match[3] ?? htmlAttribute(html, 'src')\n const src = resolveReadmeImage(rawSrc, owner, repo, base)\n if (src === null) continue\n const image: ReadmeImageParts = {\n src,\n alt: match[1] ?? htmlAttribute(html, 'alt'),\n title: match[4] ?? match[5] ?? match[6] ?? htmlAttribute(html, 'title'),\n width: numericDimension(htmlAttribute(html, 'width')),\n height: numericDimension(htmlAttribute(html, 'height')),\n }\n const heading = headings[headingIndex]?.text ?? ''\n const nearby = markdown.slice(Math.max(0, match.index - 100), Math.min(markdown.length, match.index + match[0].length + 100))\n const candidate: ScreenshotCandidate = {\n src,\n semanticScore: readmeSemanticScore(image, heading, nearby, order, match.index),\n order,\n curated: false,\n }\n const previous = found.get(src)\n if (previous === undefined || candidate.semanticScore > previous.semanticScore) found.set(src, candidate)\n order += 1\n }\n return [...found.values()]\n .filter(candidate => candidate.semanticScore >= 20)\n .sort((a, b) => b.semanticScore - a.semanticScore || a.order - b.order)\n .slice(0, MAX_SCREENSHOTS)\n}\n\n/** Ranked README image URLs; retained as the simple public extraction API. */\nexport function extractReadmeImages(markdown: string, owner: string, repo: string, subpath: string | null): string[] {\n return extractReadmeImageCandidates(markdown, owner, repo, subpath).map(candidate => candidate.src)\n}\n\n/**\n * Score dimensions from a 240px-high, no-upscale probe.\n *\n * A theme preview should resemble a complete desktop surface: landscape,\n * neither a narrow crop nor a panoramic strip, and large enough to inspect.\n * Small square logos and portrait fragments intentionally return null.\n */\nexport function previewDimensionScore(width: number, height: number): number | null {\n if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return null\n const ratio = width / height\n const area = width * height\n if (width < 280 || height < 150 || area < 48_000 || ratio < 1.05 || ratio > 3.2) return null\n let score = Math.min(28, Math.round(area / 4_000))\n if (ratio >= 1.35 && ratio <= 2.05) score += 48\n else if (ratio >= 1.18 && ratio <= 2.4) score += 28\n else score += 8\n if (width >= 320 && height >= 180) score += 14\n return score\n}\n\n/** Combine README semantics with measured geometry and return the best set. */\nexport function rankThemeScreenshots(\n candidates: ScreenshotCandidate[],\n measurements: ScreenshotMeasurement[],\n): string[] {\n const bySrc = new Map(measurements.map(item => [item.src, item]))\n return candidates.flatMap(candidate => {\n const measured = bySrc.get(candidate.src)\n if (measured === undefined) return []\n const dimensionScore = previewDimensionScore(measured.width, measured.height)\n return dimensionScore === null ? [] : [{ candidate, score: candidate.semanticScore + dimensionScore }]\n }).sort((a, b) => b.score - a.score || a.candidate.order - b.candidate.order)\n .slice(0, MAX_SCREENSHOTS)\n .map(item => item.candidate.src)\n}\n\nconst readmeShotsCache = new Map>()\n\n/** Test hook: the cache is module-level and outlives component unmounts. */\nexport function resetScreenshotsCache(): void {\n readmeShotsCache.clear()\n}\n\n/**\n * Screenshot candidates for a plugin: the registry's curated list when\n * present, otherwise lazily extracted and semantically ranked from README.\n */\nexport function pluginScreenshotCandidates(plugin: RegistryPlugin): Promise {\n const curated = safeScreenshots(plugin.screenshots)\n if (curated.length > 0) {\n return Promise.resolve(curated.map((src, order) => ({ src, order, semanticScore: 1_000 - order, curated: true })))\n }\n const m = /^https:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\/tree\\/[^/]+\\/(.+?))?\\/?$/.exec(plugin.url)\n if (m === null) return Promise.resolve([])\n const [, owner, repo, subpath = null] = m\n const cacheKey = plugin.url\n const cached = readmeShotsCache.get(cacheKey)\n if (cached !== undefined) return cached\n const fetchReadme = async (path: string | null): Promise => {\n try {\n const res = await fetch(githubUrl(`https://raw.githubusercontent.com/${owner}/${repo}/HEAD/${path === null ? '' : path + '/'}README.md`))\n return res.ok ? await res.text() : null\n } catch {\n return null\n }\n }\n const task = (async () => {\n // Monorepo subpath entries prefer their own README, falling back to the\n // repo root; shots in the subpath README resolve against its directory.\n const sub = subpath === null ? null : await fetchReadme(subpath)\n if (sub !== null) return extractReadmeImageCandidates(sub, owner!, repo!, subpath)\n const root = await fetchReadme(null)\n return root === null ? [] : extractReadmeImageCandidates(root, owner!, repo!, null)\n })().catch(() => [] as ScreenshotCandidate[])\n readmeShotsCache.set(cacheKey, task)\n return task\n}\n\n/** Screenshot URLs for dialogs; theme covers use the richer candidate API. */\nexport async function pluginScreenshots(plugin: RegistryPlugin): Promise {\n return (await pluginScreenshotCandidates(plugin)).map(candidate => candidate.src)\n}\n\n/**\n * The human-readable part of a failed command's output.\n *\n * pnpm's ndjson reporter writes one JSON object per progress tick, and a\n * large `github:` download emits thousands of them. When a failure matches\n * none of the known signatures there is no diagnosis to show, so the UI\n * falls back to the tail of stdout/stderr — which for exactly that case is\n * 600 characters of `{\"name\":\"pnpm:fetching-progress\",\"downloaded\":…}`.\n * The user is handed machine noise at the one moment they need a sentence\n * (#148, and the same shape behind #161).\n *\n * Progress objects are dropped; anything else — including JSON carrying a\n * real message — is kept, because an unrecognized failure is precisely when\n * throwing information away is most expensive.\n */\nexport function humanOutput(raw: string): string {\n const lines = raw.split(/\\r?\\n/)\n const kept: string[] = []\n for (const line of lines) {\n const trimmed = line.trim()\n if (trimmed === '') continue\n if (!trimmed.startsWith('{')) { kept.push(line); continue }\n try {\n const parsed = JSON.parse(trimmed) as { name?: unknown; err?: unknown; message?: unknown }\n const name = typeof parsed.name === 'string' ? parsed.name : ''\n // Keep anything that carries a diagnosis, drop pure progress chatter.\n if (parsed.err !== undefined || typeof parsed.message === 'string') { kept.push(line); continue }\n if (name.startsWith('pnpm:')) continue\n kept.push(line)\n } catch {\n kept.push(line)\n }\n }\n return kept.join('\\n').trim()\n}\n\n/**\n * The plugin's own name, for display.\n *\n * The catalog's `name` is an IDENTITY, and for the 104 entries that live in\n * a repository holding several plugins it is a compound one:\n * `dsh-web#packages/dsh-web-all`. Shown verbatim it puts a repository\n * path in front of a user who did not ask about repositories — and worse, it\n * disagrees with the market's own installed list, which reads names out of\n * the profile manifest and calls the same plugin `dsh-web-all`. The same\n * thing had two names either side of the Install button.\n *\n * A card answers two questions: who made it, and what is it called. The\n * author is drawn beside their avatar as one unit, so the title is free to\n * be just the plugin. Duplicate titles across authors are fine — the byline\n * is what separates them — which is why this does not try to keep the\n * repository as a qualifier.\n *\n * The repository name IS the plugin name in the ordinary case, because a\n * repository holding one plugin is named after it. Only the compound form\n * needs unpicking, and its last segment is the plugin's own directory.\n *\n * Not a substitute for the identity: every key, lookup and install still\n * uses `name` unchanged.\n */\nexport function pluginName(name: string): string {\n const hash = name.indexOf('#')\n if (hash === -1) return name\n const sub = name.slice(hash + 1)\n const leaf = sub.slice(sub.lastIndexOf('/') + 1)\n // A sub-path that is empty or trailing-slashed tells us nothing; the\n // repository half is a better answer than an empty title.\n return leaf === '' ? name.slice(0, hash) : leaf\n}\n\n/**\n * Compact display for a count that can run into the tens of thousands\n * (npm downloads, star counts): \"11.9k\" instead of \"11862\". Reported —\n * the raw number made the card byline visibly cramped once downloads was\n * added alongside stars.\n *\n * Below 1000 the exact number is shown; a small count is exactly the case\n * where the precision matters and abbreviating it buys nothing.\n */\nexport function formatCount(n: number): string {\n if (!Number.isFinite(n) || n < 1000) return String(n)\n const k = Math.round(n / 100) / 10\n return `${Number.isInteger(k) ? k.toFixed(0) : k.toFixed(1)}k`\n}\n","/**\n * Post-reload confirmation via the official Toast primitive: shown once after\n * the refresh that follows a hot install or theme switch, so the user lands\n * back in their flow with visible proof.\n */\nimport { useState } from 'react'\nimport { IconSparkle16, Toast } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { readSession } from './market-data.ts'\nimport type { Translate } from './market-data.ts'\n\nexport function InstallToast(props: { t: Translate }) {\n const t = props.t\n const [mode] = useState(() => {\n const value = sessionStorage.getItem('dshm-toast-mode')\n sessionStorage.removeItem('dshm-toast-mode')\n return value\n })\n const [names, setNames] = useState(() => {\n const value = readSession('dshm-toast')\n sessionStorage.removeItem('dshm-toast')\n return Array.isArray(value) ? value : []\n })\n if (names.length === 0) return null\n return (\n }\n onDone={() => setNames([])}\n />\n )\n}\n","/**\n * giscus wiring for the in-market comment thread.\n *\n * A plugin has ONE discussion, whether the reader reached it from\n * dshmarket.com, from the awesome-dsh-plugin catalog, or from inside the\n * market itself. That is only true as long as three things agree across the\n * three surfaces: the repository ids below, the `plugin:` term, and the\n * slug those terms are built from. The ids here mirror site/comments.mjs and\n * the slug mirrors scripts/build-site.mjs; both are asserted in\n * tests/client/comments.client.spec.ts against the real files, because a\n * silent disagreement does not fail — it forks the conversation in half and\n * nobody notices until someone asks why their comment vanished.\n */\n\nexport const GISCUS = Object.freeze({\n repo: 'awesome-dsh-plugin/awesome-dsh-plugin',\n repoId: 'R_kgDOT3ajCQ',\n category: 'Plugins',\n categoryId: 'DIC_kwDOT3ajCc4DES8_',\n})\n\n/**\n * The plugin's slug, derived from its GitHub URL exactly as the two site\n * builders derive it. Entries that live in a subdirectory of a monorepo\n * (`.../tree//packages/x`) get the `owner/repo--packages-x` form, so two\n * plugins from one repository do not share a thread.\n */\nexport function pluginSlug(url: string): string {\n const repoPath = url.replace('https://github.com/', '')\n const repo = repoPath.split('/').slice(0, 2).join('/')\n const sub = repoPath.includes('/tree/')\n ? repoPath.split('/tree/')[1]!.replace(/^[^/]+\\//, '')\n : null\n return sub ? `${repo}--${sub.replaceAll('/', '-')}` : repo\n}\n\n/** The giscus discussion term for a plugin. */\nexport const commentsTerm = (url: string): string => `plugin:${pluginSlug(url)}`\n\n/** giscus's own locale codes, which are not the ones this plugin uses. */\nexport const giscusLang = (lang: string): string => (lang === 'zh' ? 'zh-CN' : 'en')\n","/**\n * The comment thread for one plugin, rendered inside the market.\n *\n * Opening this is the request to read the comments, so it loads on open —\n * there is no second click to confirm what the first one already said. What\n * the reader does have to be told, once and plainly, is that reading here\n * contacts giscus.app and GitHub: nothing else in the market talks to a third\n * party at the reader's request, and a comment box that quietly opens that\n * connection would be a surprise, not a feature.\n *\n * The thread itself lives in GitHub Discussions, keyed on the same\n * `plugin:` term the two websites use — see comments.ts.\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport { Button, Modal } from '@deepseek-ai/dsh-client-ui-primitives'\nimport css from './Market.module.css'\nimport { GISCUS, commentsTerm, giscusLang } from './comments.ts'\n\n/**\n * Whether the host is currently showing a dark theme.\n *\n * Read off the rendered page rather than from `prefers-color-scheme`: the host\n * lets the reader pick a theme (including custom ones) independently of the\n * OS, and a light comment box dropped into a dark window is the kind of seam\n * that makes an embed look bolted on.\n */\nfunction hostIsDark(): boolean {\n if (typeof window === 'undefined') return false\n const bg = getComputedStyle(document.body).backgroundColor\n const m = /rgba?\\(\\s*(\\d+)[,\\s]+(\\d+)[,\\s]+(\\d+)/.exec(bg)\n if (!m) return window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false\n const [r, g, b] = [Number(m[1]), Number(m[2]), Number(m[3])]\n return (0.299 * r + 0.587 * g + 0.114 * b) < 128\n}\n\nexport function CommentsModal(props: {\n name: string\n url: string\n lang: string\n onClose: () => void\n t: (k: string) => string\n}) {\n const { name, url, lang, onClose, t } = props\n const mount = useRef(null)\n const [state, setState] = useState<'loading' | 'ready' | 'failed'>('loading')\n // Bumping this re-runs the effect, which is what \"retry\" means here.\n const [attempt, setAttempt] = useState(0)\n\n useEffect(() => {\n const host = mount.current\n if (host === null) return\n host.replaceChildren()\n setState('loading')\n\n const script = document.createElement('script')\n script.src = 'https://giscus.app/client.js'\n script.async = true\n script.crossOrigin = 'anonymous'\n Object.assign(script.dataset, {\n repo: GISCUS.repo,\n repoId: GISCUS.repoId,\n category: GISCUS.category,\n categoryId: GISCUS.categoryId,\n mapping: 'specific',\n term: commentsTerm(url),\n // `strict` hashes the term, so `owner/repo` cannot collide with a\n // `owner/repo--sub` entry from the same repository.\n strict: '1',\n reactionsEnabled: '1',\n emitMetadata: '0',\n inputPosition: 'bottom',\n theme: hostIsDark() ? 'dark' : 'light',\n lang: giscusLang(lang),\n loading: 'lazy',\n })\n script.onload = () => setState('ready')\n script.onerror = () => { script.remove(); setState('failed') }\n host.append(script)\n\n return () => { host.replaceChildren() }\n }, [url, lang, attempt])\n\n return (\n \n

{t('commentsNote')}

\n {state === 'failed'\n ? (\n
\n )\n : (\n

\n {state === 'loading' ? t('commentsLoading') : ''}\n

\n )}\n
\n \n )\n}\n","/**\n * The operations model behind the market's activity panel: one record per\n * mutating operation (install, update, uninstall), from the moment it is\n * queued until the user clears it.\n *\n * It exists because operation state used to live on the card that started it,\n * which disappears the moment the user paginates, searches, or switches tab —\n * taking any decision attached to it, such as a loader-id clash, along with\n * it. Records live here instead, keyed by their own id, so the card only has\n * to ask \"is anything running for this url\".\n *\n * Every function is pure so the queue, the aggregate counts, and the\n * three-way visual grouping can be tested without a host or a DOM.\n */\n\n/** What a record does to the profile. */\nexport type OperationKind = 'install' | 'update' | 'uninstall'\n\n/**\n * Lifecycle of one operation.\n *\n * `input` is not a failure: the host already reverted the change (a clash is\n * detected after pnpm resolves, and the package is removed on the spot), so\n * the profile is intact and the queue moves on. It means the operation cannot\n * finish until the user picks an outcome.\n */\nexport type OperationState = 'queued' | 'running' | 'input' | 'done' | 'warned' | 'failed'\n\n/** The installed plugins one candidate clashed with, one entry per owner. */\nexport interface ConflictGroup {\n owner: string\n ids: string[]\n}\n\nexport interface OperationRecord {\n /** Stable across re-renders; how actions address a record. */\n id: string\n kind: OperationKind\n /** Package name, or the catalog name for an install. */\n name: string\n /** Catalog url, present for installs — what a card is keyed by. */\n url?: string\n state: OperationState\n /** 0-100 while running; null when the host reports no total to divide by. */\n percent?: number | null\n /** The host's latest progress line, shown verbatim under the bar. */\n detail?: string\n /** Set with `input`: the installed plugins that hold the same entry ids. */\n conflicts?: ConflictGroup[]\n /** Set with `warned` or `failed`: one sentence naming what went wrong. */\n reason?: string\n /**\n * Set with `failed` when pnpm refused to run a dependency's build scripts.\n * The way out is one click, so it belongs on the record that reports the\n * failure — the approval banner lives elsewhere on the page, and a reader\n * looking at this row was told to press a button they could not see (#314).\n */\n blockedBuilds?: string[]\n /** Set with `done` when the change needs a page refresh to be visible. */\n needsRefresh?: boolean\n}\n\n/**\n * Visual grouping. Six states are what the code has to distinguish; three are\n * what the panel shows, because a reader scanning icons and colors cannot\n * hold six apart. The distinction inside a bucket is carried by the record's\n * own status line, not by another color.\n */\nexport type OperationBucket = 'busy' | 'ok' | 'attention'\n\nconst BUCKETS: Record = {\n queued: 'busy',\n running: 'busy',\n input: 'attention',\n failed: 'attention',\n done: 'ok',\n warned: 'ok',\n}\n\n/** Which of the three visual groups a state belongs to. */\nexport function bucketOf(state: OperationState): OperationBucket {\n return BUCKETS[state]\n}\n\n/** Whether a record has stopped moving on its own. */\nexport function isSettled(record: OperationRecord): boolean {\n return record.state === 'done' || record.state === 'warned' || record.state === 'failed'\n}\n\n/** Whether a record is waiting on the user rather than on the host. */\nexport function needsUser(record: OperationRecord): boolean {\n return record.state === 'input'\n}\n\n/** Append a record. Ids are supplied by the caller so tests stay deterministic. */\nexport function enqueue(list: readonly OperationRecord[], record: OperationRecord): OperationRecord[] {\n return [...list, record]\n}\n\n/**\n * Apply changes to one record.\n * @returns a new list; unchanged (same contents) when no record matches.\n */\nexport function patch(\n list: readonly OperationRecord[], id: string, changes: Partial>,\n): OperationRecord[] {\n return list.map(record => (record.id === id ? { ...record, ...changes } : record))\n}\n\n/** Drop one record outright — the panel's per-row dismiss. */\nexport function drop(list: readonly OperationRecord[], id: string): OperationRecord[] {\n return list.filter(record => record.id !== id)\n}\n\n/**\n * Clear finished records, keeping anything still moving or still waiting on\n * the user. A record in `input` survives: the user has not answered it yet,\n * and clearing it would delete the only route back to that decision.\n */\nexport function clearSettled(list: readonly OperationRecord[]): OperationRecord[] {\n return list.filter(record => !isSettled(record))\n}\n\n/**\n * How many queued records sit ahead of this one. The panel shows it because\n * \"queued\" without a position reads as stuck; the host runs one mutation at a\n * time, so the order shown is the order that will run.\n * @returns the count ahead, or null when the record is not queued.\n */\nexport function queuePosition(list: readonly OperationRecord[], id: string): number | null {\n const queued = list.filter(record => record.state === 'queued')\n const index = queued.findIndex(record => record.id === id)\n return index < 0 ? null : index\n}\n\nexport interface OperationSummary {\n running: number\n queued: number\n /** Records waiting on a decision. */\n attention: number\n /** Records that finished, however they finished. */\n settled: number\n /** running + queued + settled: the denominator of \"3 / 7\". */\n total: number\n /** settled + running, the numerator — a running item is the one in flight. */\n progressed: number\n}\n\n/**\n * Counts for the panel entry. The entry reports the batch, never one row:\n * one aggregate line beats seven notifications racing each other.\n */\nexport function summarize(list: readonly OperationRecord[]): OperationSummary {\n let running = 0\n let queued = 0\n let attention = 0\n let settled = 0\n for (const record of list) {\n if (record.state === 'running') running += 1\n else if (record.state === 'queued') queued += 1\n else if (needsUser(record)) attention += 1\n else settled += 1\n }\n return {\n running,\n queued,\n attention,\n settled,\n total: running + queued + settled,\n progressed: settled + running,\n }\n}\n\n/**\n * Panel order: what needs the user first, then what is still moving, then\n * what is finished. Within a group the original order is kept so a queue\n * reads top-to-bottom in the order it will run.\n */\nexport function sortForPanel(list: readonly OperationRecord[]): OperationRecord[] {\n const rank = (record: OperationRecord): number =>\n needsUser(record) ? 0 : isSettled(record) ? 2 : 1\n return [...list]\n .map((record, index) => ({ record, index }))\n .sort((a, b) => rank(a.record) - rank(b.record) || a.index - b.index)\n .map(entry => entry.record)\n}\n\n/**\n * The record a plugin card should reflect, newest first. A card shows the\n * button state for an operation in flight and a terminal marker for one that\n * ended without installing — without that marker a rejected install leaves\n * the card looking untouched, and the obvious next move is to press Install\n * again.\n */\nexport function recordForUrl(list: readonly OperationRecord[], url: string): OperationRecord | null {\n for (let index = list.length - 1; index >= 0; index -= 1) {\n const record = list[index]\n if (record.url === url) return record\n }\n return null\n}\n","/**\n * The activity entry and its panel: every install, update and uninstall the\n * user started, with the action each outcome calls for.\n *\n * The entry lives in the tab row rather than above the plugin grid, so\n * paginating, searching or switching tab cannot take a record — or a pending\n * decision — off screen. It reports the batch as one aggregate (\"installing\n * 3 / 7\") instead of one line per plugin.\n */\n\nimport {\n Button,\n IconCheckOutline16,\n IconLoadingOutline16,\n IconWarningOutline16,\n IconChevronDownOutline14,\n IconChevronUpOutline14,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport { useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport css from './Market.module.css'\nimport type { Translate } from './market-data.ts'\nimport type { ConflictGroup, OperationRecord } from './operations.ts'\nimport { bucketOf, isSettled, needsUser, queuePosition, sortForPanel, summarize } from './operations.ts'\n\n/** What the two clash outcomes are, in the order they are offered. */\nconst CHOICES = [\n { id: 'keep' as const, label: 'conflictKeep', note: 'conflictKeepNote' },\n { id: 'swap' as const, label: 'conflictSwap', note: 'conflictSwapNote' },\n]\n\n/**\n * How the panel renders a plugin it only knows by package name: the catalog\n * (or the installed list) supplies the author and avatar a card would show.\n */\nexport type DescribePlugin = (name: string) => {\n title: string\n author?: string | undefined\n avatar?: ReactNode\n}\n\nexport interface OperationsPanelProps {\n t: Translate\n /** Resolves a package name to the identity a card would show for it. */\n describe: DescribePlugin\n records: readonly OperationRecord[]\n open: boolean\n /** Owned by the parent: a card's \"cannot install\" marker raises the panel. */\n onOpenChange: (open: boolean) => void\n /** True while a swap is running, which disables both clash outcomes. */\n replacing: boolean\n /** Blocks the destructive outcome when pnpm is not usable yet. */\n envReady: boolean\n onClearSettled: () => void\n onCancel: (record: OperationRecord) => void\n onDismiss: (record: OperationRecord) => void\n onRefresh: () => void\n /** Resolve a clash: keep what is installed, or uninstall it and retry. */\n onResolveConflict: (record: OperationRecord, choice: 'keep' | 'swap') => void\n /** Retry an operation the host refused for a fixable reason. */\n onRetry?: ((record: OperationRecord) => void) | undefined\n /** Approve the build scripts pnpm blocked, then retry — replaces the plain\n * retry on a record that failed for that reason, so the fix sits next to\n * the sentence describing the problem. */\n onApproveBuilds?: ((record: OperationRecord) => void) | undefined\n}\n\n/**\n * What each plugin ends up as under the selected outcome.\n *\n * The consequence is drawn ON the plugins rather than described beside them:\n * pick \"keep what I have\" and the installed rows tick while the candidate\n * crosses out; pick the other and they swap. The candidate is in this list\n * for the same reason — a decision about which plugin survives has to show\n * what happens to the one being installed, not only to the others.\n */\nfunction OutcomePreview(props: {\n t: Translate\n record: OperationRecord\n choice: 'keep' | 'swap'\n describe: DescribePlugin\n}) {\n const { t, record, choice } = props\n const swap = choice === 'swap'\n const candidate = props.describe(record.name)\n const row = (key: string, info: ReturnType, kept: boolean, tag: string) => (\n
\n {info.avatar}\n \n {info.title}\n {info.author !== undefined && {info.author}}\n \n \n {kept ? '✓' : '✕'} {tag}\n \n
\n )\n return (\n
\n {row(record.name, candidate, swap, t(swap ? 'conflictOutcomeInstall' : 'conflictOutcomeSkip'))}\n
\n {(record.conflicts ?? []).map(group =>\n row(group.owner, props.describe(group.owner), !swap, t(swap ? 'conflictOutcomeRemove' : 'conflictOutcomeKeep')))}\n
\n )\n}\n\n/**\n * The decision attached to a clash. Two outcomes rather than an error plus a\n * destructive button: the default changes nothing, and selecting the other\n * one is itself the consent step, so its cost is stated here.\n */\nfunction ConflictChoice(props: {\n t: Translate\n record: OperationRecord\n replacing: boolean\n envReady: boolean\n describe: DescribePlugin\n onResolve: (choice: 'keep' | 'swap') => void\n}) {\n const { t, record, replacing, envReady } = props\n const [choice, setChoice] = useState<'keep' | 'swap'>('keep')\n const [whyOpen, setWhyOpen] = useState(false)\n return (\n
\n

{t('conflictBody')}

\n \n {/* Native radios: the platform already draws the control, groups it by\n `name`, and gives arrow-key navigation. A hand-rolled ring gets the\n look wrong and the keyboard behaviour with it. */}\n
\n {CHOICES.map(({ id, label, note }) => (\n \n setChoice(id)}\n />\n \n {t(label)}\n {t(note) !== '' && {t(note)}}\n \n \n ))}\n
\n {/* Both ways out of the decision share one row: the quiet one that\n reveals evidence, and the one that commits. */}\n
\n setWhyOpen(open => !open)}\n >\n {t('conflictDetails')}\n {whyOpen ? : }\n \n \n props.onResolve(choice)}\n >{replacing ? t('conflictReplacing') : t('confirm')}\n
\n {whyOpen && (\n
\n {(record.conflicts ?? []).map(group => (\n
{group.owner}: {group.ids.join(', ')}
\n ))}\n
{t('conflictWhy')}
\n
\n )}\n
\n )\n}\n\n/** Icon for a record's visual bucket — three, not one per state. */\nfunction BucketIcon(props: { record: OperationRecord }) {\n const bucket = bucketOf(props.record.state)\n if (bucket === 'busy') {\n return props.record.state === 'running'\n ? \n : \n }\n if (bucket === 'ok') return \n return \n}\n\n/** The one-line status under a record's name; the bucket carries the rest. */\nfunction statusLine(t: Translate, record: OperationRecord, ahead: number | null): string {\n switch (record.state) {\n case 'queued':\n return ahead === null || ahead === 0 ? t('opQueued') : `${t('opQueued')} · ${t('opQueuedAhead')} ${String(ahead)}`\n case 'running':\n return record.detail ?? t('opRunning')\n case 'input':\n return t('opNeedsChoice')\n case 'failed':\n return record.reason ?? t('installFail')\n case 'warned':\n return record.reason ?? t('opDone')\n case 'done':\n return record.needsRefresh === true ? t('opDoneRefresh') : t('opDone')\n }\n}\n\nexport function OperationsPanel(props: OperationsPanelProps) {\n const { t, records, open } = props\n const setOpen = props.onOpenChange\n const wrapRef = useRef(null)\n const summary = summarize(records)\n const busy = summary.running + summary.queued > 0\n\n // Dismissing a popover by pressing the control that opened it is the one\n // route nobody looks for. Escape and an outside click are; the header also\n // carries an explicit collapse for anyone who wants a target to aim at.\n // The listener covers the whole wrapper, button included, so the button's\n // own toggle is not undone by this closing first.\n useEffect(() => {\n if (!open) return undefined\n const onKey = (event: KeyboardEvent) => { if (event.key === 'Escape') setOpen(false) }\n const onPointer = (event: MouseEvent) => {\n const wrap = wrapRef.current\n if (wrap !== null && !wrap.contains(event.target as Node)) setOpen(false)\n }\n document.addEventListener('keydown', onKey)\n document.addEventListener('mousedown', onPointer)\n return () => {\n document.removeEventListener('keydown', onKey)\n document.removeEventListener('mousedown', onPointer)\n }\n }, [open, setOpen])\n\n // The entry label is the batch, not a verb with no object: a bare \"3\" says\n // nothing about what is happening to the profile.\n const label = busy\n ? `${t('opInstalling')} ${String(summary.progressed)}/${String(summary.total)}`\n : summary.attention > 0\n ? `${String(summary.attention)} ${t('opNeedsYou')}`\n : t('opTitle')\n\n // Always render inside .opWrap so the tab-row bottom margin is stable —\n // the old idle-only \n {open && (\n
\n
\n {t('opTitle')}\n \n {summary.settled > 0 && (\n \n )}\n setOpen(false)}\n >\n
\n {busy && (\n
\n
\n {t('opInstalling')} {summary.progressed}/{summary.total}\n
\n
\n \n
\n {/* Long installs are the norm; saying so is what makes leaving\n the page an option rather than a gamble. */}\n
{t('opLeaveHint')}
\n
\n )}\n {records.length === 0 && (\n
\n {t('opEmpty')}\n
{t('opEmptyHint')}
\n
\n )}\n {sortForPanel(records).map((record) => {\n const ahead = queuePosition(records, record.id)\n return (\n
\n \n
\n
\n {t(`opKind_${record.kind}`)}\n {record.name}\n
\n {record.state === 'running' && typeof record.percent === 'number' && (\n
\n
\n
\n )}\n
\n {statusLine(t, record, ahead)}\n
\n {needsUser(record) && (\n props.onResolveConflict(record, choice)}\n />\n )}\n
\n
\n {record.state === 'running' && (\n \n )}\n {record.state === 'queued' && (\n \n )}\n {record.state === 'done' && record.needsRefresh === true && (\n \n )}\n {record.state === 'failed' && (record.blockedBuilds ?? []).length > 0 && props.onApproveBuilds !== undefined && (\n \n )}\n {record.state === 'failed' && (record.blockedBuilds ?? []).length === 0 && props.onRetry !== undefined && (\n \n )}\n {isSettled(record) && (\n \n )}\n
\n
\n )\n })}\n
\n )}\n
\n )\n}\n","/**\n * Diagnostics sub-panel — issue #98 phase 3 (plugin presets).\n *\n * Rendered at the bottom of the Diagnostics tab inside a collapsible section\n * (the parent keeps it mounted and passes `open`, so it lazy-loads on first\n * expand and keeps its state across collapses). Lists named presets\n * (GET /dsh-market/presets), saves the current community bundle order as a\n * new preset (POST /dsh-market/presets {action:'save', name, bundleOrder,\n * disabled}), applies one (action:'apply' — the host trial-validates and\n * auto-snapshots before writing; on success the parent refreshes the check\n * report) and deletes one after an inline double confirmation\n * (action:'delete').\n *\n * The list payload is read defensively (bare array or {presets: [...]}).\n */\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { Button, Input } from '@deepseek-ai/dsh-client-ui-primitives'\nimport css from './Market.module.css'\nimport { api } from './market-data.ts'\nimport type { Translate } from './market-data.ts'\n\n/** One preset from GET /dsh-market/presets. */\nexport interface Preset {\n name: string\n bundleOrder: string[]\n disabled: string[]\n}\n\ninterface PresetPanelProps {\n t: Translate\n /** True while the parent collapsible section is expanded (lazy-load gate). */\n open: boolean\n /** Current community bundle order (the Diagnostics ordering DRAFT — what the\n * user is editing, not necessarily the last-applied order); saved verbatim. */\n bundleOrder: string[]\n /** Re-fetch the check report after a successful preset apply. */\n onRefresh: () => void\n}\n\ninterface JsonBody {\n ok?: unknown\n error?: unknown\n changes?: unknown\n}\n\nasync function postJson(url: string, body: unknown): Promise<{ status: number; body: JsonBody | null }> {\n let res: Response\n try {\n res = await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) })\n } catch {\n return { status: 0, body: null }\n }\n let payload: JsonBody | null = null\n try { payload = (await res.json()) as JsonBody } catch { /* non-JSON body */ }\n return { status: res.status, body: payload }\n}\n\nconst errorText = (status: number, body: JsonBody | null, fallback: string): string =>\n body !== null && typeof body.error === 'string'\n ? body.error\n : status === 0 ? fallback + 'network error' : fallback + `HTTP ${String(status)}`\n\nexport function PresetPanel(props: PresetPanelProps) {\n const { t, open, bundleOrder, onRefresh } = props\n const [presets, setPresets] = useState(null)\n const [error, setError] = useState(null)\n const [msg, setMsg] = useState(null)\n const [busy, setBusy] = useState(null)\n const [name, setName] = useState('')\n /** Preset name awaiting the second confirm click, or null. */\n const [confirmDelete, setConfirmDelete] = useState(null)\n /** Hidden for the import action. */\n const loaded = useRef(false)\n\n const load = useCallback(() => {\n fetch(api('/dsh-market/presets'), { cache: 'no-store' })\n .then(res => res.json())\n .then(body => {\n const list: unknown[] = Array.isArray(body) ? body : Array.isArray(body.presets) ? body.presets : []\n setPresets(list.map((item: unknown) => {\n const preset = (item ?? {}) as Preset\n return {\n name: String(preset.name ?? ''),\n bundleOrder: Array.isArray(preset.bundleOrder) ? preset.bundleOrder.map(String) : [],\n disabled: Array.isArray(preset.disabled) ? preset.disabled.map(String) : [],\n }\n }))\n setError(null)\n // Mark loaded only on success so a transient failure retries on the\n // next expand (review M3).\n loaded.current = true\n })\n .catch(() => setError(t('presetListFail') + 'network'))\n }, [t])\n\n useEffect(() => {\n if (open && !loaded.current) {\n load()\n }\n }, [open, load])\n\n const save = useCallback(() => {\n const presetName = name.trim()\n if (presetName === '') {\n setError(t('presetNameEmpty'))\n return\n }\n if (busy !== null) return\n setBusy('save')\n setMsg(null)\n setError(null)\n // Save the CURRENT disable list too — applying a preset restores it, so\n // saving an empty list would silently re-enable every disabled plugin.\n fetch(api('/dsh-market/installed'), { cache: 'no-store' })\n .then(res => res.json())\n .then((installed: { disabled?: unknown }) => postJson(api('/dsh-market/presets'), {\n action: 'save',\n name: presetName,\n bundleOrder,\n disabled: Array.isArray(installed.disabled) ? installed.disabled.map(String) : [],\n }))\n .then(({ status, body }) => {\n if (status >= 200 && status < 300 && body?.ok === true) {\n setName('')\n setMsg(t('presetSaved'))\n load()\n } else {\n setError(errorText(status, body, t('presetFail')))\n }\n })\n .catch(() => setError(t('presetFail') + 'network'))\n .finally(() => setBusy(null))\n }, [busy, bundleOrder, load, name, t])\n\n const apply = useCallback((presetName: string) => {\n if (busy !== null) return\n setBusy('apply')\n setMsg(null)\n setError(null)\n postJson(api('/dsh-market/presets'), { action: 'apply', name: presetName })\n .then(({ status, body }) => {\n if (status >= 200 && status < 300 && body?.ok === true) {\n setMsg(t('presetApplied'))\n onRefresh()\n } else {\n setError(errorText(status, body, t('presetFail')))\n }\n })\n .catch(() => setError(t('presetFail') + 'network'))\n .finally(() => setBusy(null))\n }, [busy, onRefresh, t])\n\n const remove = useCallback((presetName: string) => {\n if (busy !== null) return\n setBusy('delete')\n setMsg(null)\n setError(null)\n postJson(api('/dsh-market/presets'), { action: 'delete', name: presetName })\n .then(({ status, body }) => {\n if (status >= 200 && status < 300 && body?.ok === true) {\n setConfirmDelete(null)\n setMsg(t('presetDeleted'))\n load()\n } else {\n setError(errorText(status, body, t('presetFail')))\n }\n })\n .catch(() => setError(t('presetFail') + 'network'))\n .finally(() => setBusy(null))\n }, [busy, load, t])\n\n /** Change preview of one preset: {reordered[], enabled[], disabled[], noop}. */\n const [previewed, setPreviewed] = useState<{ name: string; changes: { reordered: string[]; enabled: string[]; disabled: string[]; noop: boolean } } | null>(null)\n const preview = useCallback((presetName: string) => {\n if (busy !== null) return\n setBusy('preview')\n setMsg(null)\n setError(null)\n if (previewed?.name === presetName) {\n setPreviewed(null)\n setBusy(null)\n return\n }\n postJson(api('/dsh-market/presets'), { action: 'preview', name: presetName })\n .then(({ status, body }) => {\n if (status >= 200 && status < 300 && body?.ok === true && body.changes !== null && typeof body.changes === 'object') {\n const changes = body.changes as { reordered?: unknown; enabled?: unknown; disabled?: unknown; noop?: unknown }\n setPreviewed({\n name: presetName,\n changes: {\n reordered: Array.isArray(changes.reordered) ? changes.reordered.map(String) : [],\n enabled: Array.isArray(changes.enabled) ? changes.enabled.map(String) : [],\n disabled: Array.isArray(changes.disabled) ? changes.disabled.map(String) : [],\n noop: changes.noop === true,\n },\n })\n } else {\n setError(errorText(status, body, t('presetPreviewFail')))\n }\n })\n .catch(() => setError(t('presetPreviewFail') + 'network'))\n .finally(() => setBusy(null))\n }, [busy, previewed, t])\n\n return (\n
\n

{t('presetHint')}

\n\n {error !== null &&
{error}
}\n\n {presets === null || presets.length === 0\n ?
{t('presetEmpty')}
\n : (\n
\n {presets.map(preset => (\n
\n
\n {preset.name}\n {preset.bundleOrder.length > 0 && (\n {t('presetBundleCount').replace('{0}', String(preset.bundleOrder.length))}\n )}\n \n {confirmDelete === preset.name ? (\n \n \n \n \n ) : (\n \n \n \n \n \n )}\n
\n {previewed?.name === preset.name && (\n
\n {t('presetPreviewTitle')}\n {previewed.changes.noop\n ?
{t('presetNoop')}
\n : (\n
\n {previewed.changes.reordered.length > 0\n &&
{t('presetReorder').replace('{0}', String(previewed.changes.reordered.length))}: {previewed.changes.reordered.join(', ')}
}\n {previewed.changes.enabled.length > 0\n &&
{t('presetEnable').replace('{0}', String(previewed.changes.enabled.length))}: {previewed.changes.enabled.join(', ')}
}\n {previewed.changes.disabled.length > 0\n &&
{t('presetDisable').replace('{0}', String(previewed.changes.disabled.length))}: {previewed.changes.disabled.join(', ')}
}\n
\n )}\n
\n )}\n
\n ))}\n
\n )}\n\n {msg !== null &&
{msg}
}\n
\n )\n}\n","/**\n * Diagnostics sub-panel — issue #98 phase 3 (snapshots & rollback).\n *\n * Rendered at the bottom of the Diagnostics tab inside a collapsible section\n * (the parent keeps it mounted and passes `open`, so it lazy-loads on first\n * expand and keeps its state across collapses). Lists profile snapshots\n * (GET /dsh-market/snapshots), creates one (POST /dsh-market/snapshots) and\n * restores one after an inline double confirmation\n * (POST /dsh-market/restore-snapshot {snapshot}).\n *\n * The list payload is read defensively (bare array or {snapshots: [...]});\n * `files` entries are {path, json|lines} objects, from which only `path` is\n * displayed.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { Button } from '@deepseek-ai/dsh-client-ui-primitives'\nimport css from './Market.module.css'\nimport { api } from './market-data.ts'\nimport type { Translate } from './market-data.ts'\n\n/** One snapshot from GET /dsh-market/snapshots. */\nexport interface Snapshot {\n id: string\n createdAt: string | number\n files: string[]\n}\n\ninterface SnapshotPanelProps {\n t: Translate\n /** True while the parent collapsible section is expanded (lazy-load gate). */\n open: boolean\n /** Re-fetch the check report after a successful restore. */\n onRefresh: () => void\n}\n\ninterface JsonBody { ok?: unknown; error?: unknown }\n\nasync function postJson(url: string, body: unknown): Promise<{ status: number; body: JsonBody | null }> {\n let res: Response\n try {\n res = await fetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) })\n } catch {\n return { status: 0, body: null }\n }\n let payload: JsonBody | null = null\n try { payload = (await res.json()) as JsonBody } catch { /* non-JSON body */ }\n return { status: res.status, body: payload }\n}\n\n/** Snapshot file entries may be plain names or {path, json|lines} objects. */\nfunction fileNames(files: unknown): string[] {\n if (!Array.isArray(files)) return []\n const names: string[] = []\n for (const file of files) {\n if (typeof file === 'string') {\n names.push(file)\n } else if (file !== null && typeof file === 'object') {\n const path = (file as Record).path\n if (typeof path === 'string') names.push(path)\n }\n }\n return names\n}\n\nfunction snapshotOf(value: unknown): Snapshot | null {\n if (value === null || typeof value !== 'object') return null\n const item = value as Record\n if (typeof item.id !== 'string' && typeof item.id !== 'number') return null\n return {\n id: String(item.id),\n createdAt: typeof item.createdAt === 'string' || typeof item.createdAt === 'number' ? item.createdAt : 0,\n files: fileNames(item.files),\n }\n}\n\nfunction formatTime(value: string | number): string {\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString()\n}\n\nexport function SnapshotPanel(props: SnapshotPanelProps) {\n const { t, open, onRefresh } = props\n const [snapshots, setSnapshots] = useState(null)\n const [error, setError] = useState(null)\n const [msg, setMsg] = useState(null)\n const [busy, setBusy] = useState(null)\n /** Snapshot id awaiting the second confirm click (restore), or null. */\n const [confirmId, setConfirmId] = useState(null)\n /** Snapshot id awaiting the second confirm click (delete), or null. */\n const [confirmDeleteId, setConfirmDeleteId] = useState(null)\n const loaded = useRef(false)\n\n const load = useCallback(() => {\n fetch(api('/dsh-market/snapshots'), { cache: 'no-store' })\n .then(res => res.json())\n .then(body => {\n const list: unknown[] = Array.isArray(body) ? body : Array.isArray(body.snapshots) ? body.snapshots : []\n setSnapshots(list.map(snapshotOf).filter((snap): snap is Snapshot => snap !== null))\n setError(null)\n // Mark loaded only on success so a transient failure retries on the\n // next expand (review M3).\n loaded.current = true\n })\n .catch(() => setError(t('snapListFail') + 'network'))\n }, [t])\n\n useEffect(() => {\n if (open && !loaded.current) {\n load()\n }\n }, [open, load])\n\n const create = useCallback(() => {\n if (busy !== null) return\n setBusy('create')\n setMsg(null)\n setError(null)\n postJson(api('/dsh-market/snapshots'), {})\n .then(({ status, body }) => {\n if (status >= 200 && status < 300 && body?.ok === true) {\n setMsg(t('snapCreated'))\n load()\n } else {\n const detail = body !== null && typeof body.error === 'string'\n ? body.error\n : status === 0 ? 'network error' : `HTTP ${String(status)}`\n setError(t('snapCreateFail') + detail)\n }\n })\n .catch(() => setError(t('snapCreateFail') + 'network'))\n .finally(() => setBusy(null))\n }, [busy, load, t])\n\n const restore = useCallback((id: string) => {\n if (busy !== null) return\n setBusy('restore')\n setMsg(null)\n setError(null)\n postJson(api('/dsh-market/restore-snapshot'), { snapshot: id })\n .then(({ status, body }) => {\n if (status >= 200 && status < 300 && body?.ok === true) {\n setConfirmId(null)\n setMsg(t('snapRestored'))\n onRefresh()\n } else {\n const detail = body !== null && typeof body.error === 'string'\n ? body.error\n : status === 0 ? 'network error' : `HTTP ${String(status)}`\n setError(t('snapRestoreFail') + detail)\n }\n })\n .catch(() => setError(t('snapRestoreFail') + 'network'))\n .finally(() => setBusy(null))\n }, [busy, onRefresh, t])\n\n /** Delete one snapshot after inline double confirmation. */\n const remove = useCallback((id: string) => {\n if (busy !== null) return\n setBusy('delete')\n setMsg(null)\n setError(null)\n postJson(api('/dsh-market/delete-snapshot'), { snapshot: id })\n .then(({ status, body }) => {\n if (status >= 200 && status < 300 && body?.ok === true) {\n setConfirmDeleteId(null)\n setMsg(t('snapDeleted'))\n load()\n } else {\n const detail = body !== null && typeof body.error === 'string'\n ? body.error\n : status === 0 ? 'network error' : `HTTP ${String(status)}`\n setError(t('snapDeleteFail') + detail)\n }\n })\n .catch(() => setError(t('snapDeleteFail') + 'network'))\n .finally(() => setBusy(null))\n }, [busy, load, t])\n\n return (\n
\n

{t('snapHint')}

\n\n
\n \n
\n\n {error !== null &&
{error}
}\n\n {snapshots === null || snapshots.length === 0\n ?
{t('snapEmpty')}
\n : (\n
\n {snapshots.map(snap => (\n
\n {confirmId === snap.id ? (\n <>\n

{t('snapRestoreConfirmText')}

\n
\n \n \n
\n \n ) : confirmDeleteId === snap.id ? (\n <>\n

{t('snapDeleteConfirmText')}

\n
\n \n \n
\n \n ) : (\n <>\n
\n {formatTime(snap.createdAt)}\n {snap.id}\n {snap.files.length > 0 && (\n {t('snapFiles')}: {snap.files.join(', ')}\n )}\n
\n
\n \n \n
\n \n )}\n
\n ))}\n
\n )}\n\n {msg !== null &&
{msg}
}\n
\n )\n}\n","/**\n * Diagnostics tab — issue #98: renders the profile composition check report\n * served by the host route /dsh-market/check (see src/check.ts). Below the\n * report sit the phase 2/3 action panels: a community-bundle ordering block\n * (reorder locally with ↑/↓, POST to /dsh-market/bundle-order) and a\n * snapshots & rollback panel (snapshot-panel.tsx) — collapsible, default\n * collapsed, and lazy-fetching on first expand. The plugin presets panel\n * ships in a later stacked PR.\n *\n * Read-only view of the loading-layer stack and the conflict surface: bundle\n * order (official vs community), duplicate loader entry ids, peer dependency\n * mismatches, multi-version core packages, overrides and orphan patches. The\n * report\n * shape mirrors the CheckReport interface in src/check.ts; it is re-declared\n * here because the client bundle is built independently of the host tree.\n */\nimport { useCallback, useEffect, useMemo, useRef, useState, type DragEvent, type ReactNode } from 'react'\nimport { Button, IconChevronDownOutline14, IconChevronRightOutline14, IconLoadingOutline16, IconRefreshOutline14, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'\nimport css from './Market.module.css'\nimport { api } from './market-data.ts'\nimport type { Translate } from './market-data.ts'\nimport { PresetPanel } from './preset-panel.tsx'\nimport { SnapshotPanel } from './snapshot-panel.tsx'\n\n/** Mirrors BundleLayer in src/check.ts. */\ninterface BundleLayer {\n name: string\n source: string\n kind: 'official' | 'community'\n directory: string | null\n patchPath: string | null\n error: string | null\n entries: string[]\n parseError: string | null\n}\n\n/** Mirrors DuplicateId in src/check.ts. */\ninterface DuplicateId {\n id: string\n layers: string[]\n count: number\n}\n\n/** Mirrors OverrideRow in src/check.ts. */\ninterface OverrideRow {\n id: string\n layer: string\n overriddenLayers: string[]\n}\n\n/** Mirrors OrphanRow in src/check.ts. */\ninterface OrphanRow {\n id: string\n layer: string\n reason: string\n}\n\n/** Mirrors PeerVerdict in src/check.ts. */\ntype PeerVerdict =\n | {\n kind: 'risk'\n risk: { plugin: string; peer: string; range: string; resolved: string; direction: 'belowMin' | 'aboveMax' }\n }\n | {\n kind: 'warning'\n warning: { plugin: string; peer: string; range: string; resolved: string; reason: 'aboveMax' | 'optional' }\n }\n | { kind: 'none' }\n\n/** Mirrors PeerMismatch in src/check.ts. */\ninterface PeerMismatch {\n plugin: string\n name: string\n range: string\n resolved: string | null\n satisfied: boolean | null\n optional?: boolean\n verdict?: PeerVerdict\n}\n\n/** Mirrors MultiVersion in src/check.ts. */\ninterface MultiVersion {\n name: string\n versions: string[]\n hoisted: string | null\n}\n\n/** Mirrors CheckSummary in src/check.ts. */\ninterface CheckSummary {\n ok: boolean\n errors: string[]\n warnings: string[]\n}\n\n/** Mirrors OrderConflict in src/order.ts (top-level orderConflicts in CheckReport). */\ninterface OrderConflict {\n name: string\n reason: string\n}\n\n/** Mirrors CheckReport in src/check.ts. */\ninterface CheckReport {\n profile: string\n scannedAt: number\n bundles: BundleLayer[]\n duplicates: DuplicateId[]\n overrides: OverrideRow[]\n orphans: OrphanRow[]\n peerMismatches: PeerMismatch[]\n multiVersion: MultiVersion[]\n summary: CheckSummary\n /** #98 phase 2: validateOrder result for the CURRENT bundle order, when the host emits it. */\n orderConflicts?: OrderConflict[]\n /** #98 opt: loader rows sharing one name — informational display only, never a conflict (review #109). */\n duplicateNames?: Array<{ name: string; layers: string[]; count: number }>\n /** #98 opt: LOOT-style suggested community order satisfying every rule. */\n suggestedOrder?: { ok: true; order: string[] } | { ok: false; cycle: string[] } | null\n}\n\n/**\n * A collapsible report section: header shows title + count + chevron; the\n * body stays mounted (hidden via CSS when collapsed) so every block keeps\n * its state. ALL blocks are collapsed by default — the summary strip above\n * gives the overview, and a problem block's title is highlighted and its\n * collapsed `overview` line shows the first issue, so nothing important is\n * hidden. Expand a block to see its full content.\n */\nfunction Section(props: {\n title: string\n count: number\n empty: string\n defaultOpen?: boolean\n problem?: boolean\n overview?: ReactNode\n /** Render `children` even at count 0 (e.g. a zero-mismatch peer block whose\n * informational disclosure must stay reachable). */\n alwaysShowBody?: boolean\n children: ReactNode\n}) {\n const { title, count, empty, defaultOpen, problem = true, overview, alwaysShowBody = false, children } = props\n const [open, setOpen] = useState(defaultOpen ?? false)\n const alert = problem && count > 0\n return (\n
\n \n {!open && overview !== undefined &&
{overview}
}\n
\n {count === 0 && !alwaysShowBody ?
{empty}
: children}\n
\n
\n )\n}\n\n/** A collapsible section that KEEPS its children mounted (hidden via CSS when\n * collapsed) so the phase 3 panels below retain their loaded data and\n * in-progress edits across collapses. Children mount from the start, but the\n * panels only fetch when `open` first becomes true.\n */\nfunction CollapsibleSection(props: { title: string; count?: number; open: boolean; onToggle: () => void; children: ReactNode }) {\n const { title, count, open, onToggle, children } = props\n return (\n
\n \n
\n {children}\n
\n
\n )\n}\n/** Map an orphan patch reason (src/check.ts) to a locale key for its badge. */\nfunction orphanKindLabel(reason: string): string {\n if (reason === 'insert is not an array') return 'orphanInsertNotArray'\n if (reason === 'insert target not found') return 'orphanInsertTargetMissing'\n if (reason === 'insert target is not a group') return 'orphanInsertTargetNotGroup'\n if (reason === 'id required for non-insert patch') return 'orphanIdRequired'\n if (reason === 'patch target not found') return 'orphanPatchTargetMissing'\n if (reason.startsWith('name mismatch')) return 'orphanNameMismatch'\n return 'orphanReasonOther'\n}\n\n/** Badge label for one risk-tier peer row (#201). */\nfunction peerRiskLabel(peer: PeerMismatch, t: Translate): string {\n return peer.verdict?.kind === 'risk' && peer.verdict.risk.direction === 'aboveMax'\n ? t('peerRiskAboveMax')\n : t('peerRiskBelowMin')\n}\n\n/** Badge label for one warning-tier peer row (optional / aboveMax / legacy). */\nfunction peerWarningLabel(peer: PeerMismatch, t: Translate): string {\n if (peer.verdict?.kind !== 'warning') return t('checkUnsatisfied')\n return peer.verdict.warning.reason === 'optional' ? t('peerWarnOptional') : t('peerWarnAboveMax')\n}\n\n/**\n * Fetch and render the profile check report. Refetches on every mount, so\n * switching tabs away and back re-runs the (cheap, read-only) analysis; the\n * phase 3 panels below call `refresh()` after applying changes.\n */\nexport function Diagnostics(props: { t: Translate }) {\n const { t } = props\n const [report, setReport] = useState(null)\n const [error, setError] = useState(null)\n const [orderOpen, setOrderOpen] = useState(false)\n const [explainOpen, setExplainOpen] = useState(false)\n const [snapOpen, setSnapOpen] = useState(false)\n const [presetOpen, setPresetOpen] = useState(false)\n const [fixMsg, setFixMsg] = useState(null)\n /** The built AI-fix prompt when the clipboard path failed — rendered as a\n * selectable text block so the user can still copy it manually. */\n const [fixFallback, setFixFallback] = useState(null)\n /** Bump to re-run the /dsh-market/check fetch after an order/snapshot-restore apply. */\n const [version, setVersion] = useState(0)\n const refresh = useCallback(() => setVersion(v => v + 1), [])\n\n // --- issue #98 phase 2 (step 1): community-bundle ordering ---------------\n /** Community bundle names from the report, in declared order. */\n const communityNames = useMemo(\n () => report === null ? [] : report.bundles.filter(bundle => bundle.kind === 'community').map(bundle => bundle.name),\n [report],\n )\n /** Local editing state: re-synced whenever the report (re)loads. */\n const [order, setOrder] = useState(communityNames)\n const [orderMsg, setOrderMsg] = useState(null)\n const [orderErr, setOrderErr] = useState(null)\n const [orderBusy, setOrderBusy] = useState(false)\n /** Current-vs-candidate composition diff from a rejected static-composition\n * validation (#125 review): what the candidate would change, shown as a hint. */\n const [orderDiff, setOrderDiff] = useState<{ overrides: number; orphans: number; duplicates: number } | null>(null)\n /**\n * Content identity of the last community order this draft synced to. A\n * refresh() refetch returns a NEW array even when the order is unchanged,\n * so a naive `setOrder(communityNames)` effect would wipe the user's\n * in-progress drag/↑↓ edits on every unrelated re-check. Only resync when\n * the report's community order actually CHANGED (apply order / snapshot\n * restore) — an identical refetch keeps the draft (review M2).\n */\n const syncedOrderRef = useRef(null)\n useEffect(() => {\n const synced = syncedOrderRef.current\n const same = synced !== null\n && synced.length === communityNames.length\n && communityNames.every((name, i) => name === synced[i])\n if (same) return\n syncedOrderRef.current = communityNames\n setOrder(communityNames)\n }, [communityNames])\n\n /** Swap one community bundle with its neighbour (-1 up, +1 down). */\n const moveBundle = (index: number, delta: -1 | 1) => {\n setOrder(prev => {\n const next = [...prev]\n const target = index + delta\n if (target < 0 || target >= next.length) return prev\n ;[next[index], next[target]] = [next[target]!, next[index]!]\n return next\n })\n }\n\n // --- drag & drop reordering (draft only — saved by 应用顺序 / Apply order) ---\n /** Row being dragged (index into the local `order` draft). */\n const [dragIndex, setDragIndex] = useState(null)\n /** Row currently under the pointer, highlighted as the drop target. */\n const [dragOverIndex, setDragOverIndex] = useState(null)\n\n const onRowDragStart = (index: number) => (event: DragEvent) => {\n if (orderBusy) {\n event.preventDefault()\n return\n }\n setDragIndex(index)\n event.dataTransfer?.setData?.('text/plain', order[index] ?? '')\n if (event.dataTransfer !== undefined) event.dataTransfer.effectAllowed = 'move'\n }\n\n const onRowDragOver = (index: number) => (event: DragEvent) => {\n if (dragIndex === null || dragIndex === index) return\n // preventDefault marks the row as a valid drop target (no auto-scroll).\n event.preventDefault()\n if (event.dataTransfer !== undefined) event.dataTransfer.dropEffect = 'move'\n setDragOverIndex(index)\n }\n\n const onRowDragLeave = (index: number) => () => {\n setDragOverIndex(prev => prev === index ? null : prev)\n }\n\n const onRowDrop = (index: number) => (event: DragEvent) => {\n event.preventDefault()\n const from = dragIndex\n setDragIndex(null)\n setDragOverIndex(null)\n if (from === null || from === index) return\n // Reorder the LOCAL draft only; the host is told via 应用顺序 / Apply order.\n setOrder(prev => {\n const next = [...prev]\n const [moved] = next.splice(from, 1)\n next.splice(index, 0, moved!)\n return next\n })\n }\n\n const onRowDragEnd = () => {\n setDragIndex(null)\n setDragOverIndex(null)\n }\n\n /** POST the current community order; the host statically validates the\n * candidate composition (dry-run replay) before writing. */\n const applyOrder = (target?: string[]) => {\n if (orderBusy) return\n setOrderBusy(true)\n setOrderMsg(null)\n setOrderErr(null)\n setOrderDiff(null)\n fetch(api('/dsh-market/bundle-order'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ order: target ?? order }),\n })\n .then(async (res) => {\n const body = (await res.json().catch(() => null)) as {\n ok?: unknown\n error?: unknown\n trial?: {\n errors?: Array<{ layer?: unknown; message?: unknown }>\n warnings?: unknown[]\n diff?: { overrides?: unknown[]; orphans?: unknown[]; duplicates?: unknown[] }\n }\n } | null\n if (!res.ok || body?.ok !== true) {\n // A rejected static-composition validation (422) carries the\n // current-vs-candidate diff — surface what the candidate would\n // change as an informational hint (issue #125 review).\n const diff = body?.trial?.diff\n const overrides = diff?.overrides?.length ?? 0\n const orphans = diff?.orphans?.length ?? 0\n const duplicates = diff?.duplicates?.length ?? 0\n setOrderDiff(overrides + orphans + duplicates > 0 ? { overrides, orphans, duplicates } : null)\n const firstMessage = body?.trial?.errors?.[0]?.message\n setOrderErr(body?.trial !== undefined\n ? t('orderTrialFail').replace('{0}', firstMessage !== undefined ? String(firstMessage) : '')\n : String(body?.error ?? `HTTP ${String(res.status)}`))\n return\n }\n setOrderDiff(null)\n setOrderMsg(t('orderApplied'))\n // Refetch the report so communityNames / the ordering draft reflect\n // the applied order.\n refresh()\n })\n .catch((err: unknown) => setOrderErr(err instanceof Error ? err.message : String(err)))\n .finally(() => setOrderBusy(false))\n }\n\n useEffect(() => {\n let live = true\n // Do NOT null the report here: a refresh() (manual re-check, or after an\n // order/snapshot-restore apply) must keep the previous data visible and\n // must not clobber the in-progress ordering draft, which re-syncs from\n // communityNames only when the report actually changes (review M2).\n setError(null)\n fetch(api('/dsh-market/check'), { cache: 'no-store' })\n .then(async (res) => {\n if (!res.ok) throw new Error(`HTTP ${String(res.status)}`)\n const body = (await res.json()) as CheckReport\n if (live) setReport(body)\n })\n .catch((err: unknown) => {\n if (live) setError(err instanceof Error ? err.message : String(err))\n })\n return () => { live = false }\n }, [version])\n\n if (error !== null) {\n return
{t('checkLoadFail')}{error}
\n }\n if (report === null) {\n return (\n
\n \n {t('checkLoading')}\n
\n )\n }\n\n const summary = report.summary\n const suggested = report.suggestedOrder ?? null\n // #201: confirmed mismatches are tiered by the server-side classifyPeer\n // verdict instead of the old binary `satisfied === false` split. A missing\n // verdict (older host) keeps the legacy behavior: confirmed = warning tier.\n const peerRisk = report.peerMismatches.filter(peer => peer.satisfied === false && peer.verdict?.kind === 'risk')\n const peerWarning = report.peerMismatches.filter(peer => peer.satisfied === false && (peer.verdict === undefined || peer.verdict.kind === 'warning'))\n // Classifier `none` (unparseable / * prerelease artifacts) and unevaluable\n // rows share the informational display tier; true rows stay satisfied.\n const peerInfo = report.peerMismatches.filter(peer =>\n (peer.satisfied === false && peer.verdict?.kind === 'none') || peer.satisfied === null)\n const peerSatisfied = report.peerMismatches.filter(peer => peer.satisfied === true)\n // Category counts for the overview strip: conflicts / peer tiers / order.\n // Conflicts = HARD duplicate loader entries only; same-name rows are\n // informational and stay out of the conflict count (review #109).\n const catConflict = report.duplicates.length\n const catRisk = peerRisk.length\n const catWarn = peerWarning.length\n const catInfo = peerInfo.length\n const catMulti = report.multiVersion.length\n const catOrder = report.orderConflicts?.length ?? 0\n const anyIssue = catConflict + catRisk + catWarn + catInfo + catMulti + catOrder > 0\n // AI-fix only shows for HARD issues — boot errors, duplicate entries, or a\n // directional peer RISK. Optional peers, aboveMax without an explicit\n // bound, and unknown rows are warning/info and never nudge an agent into\n // risky changes without a clear problem (#201, conservative UX).\n const hasHardIssues = summary.errors.length > 0\n || report.duplicates.length > 0\n || catRisk > 0\n\n /**\n * Build the AI-fix prompt (structured errors / duplicates / peer risks /\n * order conflicts + scope) and copy it to the clipboard. The user pastes it\n * into a new conversation and decides whether to send — the agent never runs\n * automatically.\n * (A previous auto-open/prefill attempt was dropped: it was unreliable\n * across host versions, so plain copy + toast is the contract.)\n */\n const startAgentFix = () => {\n const lines: string[] = []\n lines.push(t('aiFixIntro').replace('{0}', report.profile))\n lines.push('')\n // Self-identification guard FIRST: the prompt must make the receiving\n // agent detect whether it is itself the harness running this profile.\n // If it is, mutating the live composition — or upgrading/restarting the\n // harness or core packages, or reinstalling deps — would kill the very\n // session doing the fix, so those actions are forbidden and only a plan\n // to run externally is produced. When the agent is NOT the target harness\n // (a separate process), the conservative in-place scope below applies.\n lines.push(t('aiFixDetect'))\n lines.push('')\n lines.push(t('aiFixIfSelf'))\n lines.push('')\n // Structured prompt only (#201): boot errors, duplicate loader ids and\n // directional peer RISK rows. summary.warnings is no longer dumped — its\n // peer lines contain the whole 30+ warning/info noise this issue untangles.\n const errorLines = summary.errors.filter(line => !line.startsWith('duplicate loader entry id'))\n if (errorLines.length > 0) {\n lines.push(`${t('checkErrors')}:`)\n for (const e of errorLines) lines.push(`- ${e}`)\n lines.push('')\n }\n if (report.duplicates.length > 0) {\n lines.push(`${t('checkDuplicates')}:`)\n for (const dup of report.duplicates) lines.push(`- ${dup.id} × ${dup.count} (${dup.layers.join(' / ')})`)\n lines.push('')\n }\n if (peerRisk.length > 0) {\n lines.push(`${t('checkPeerRisk')}:`)\n for (const peer of peerRisk) {\n const direction = peer.verdict?.kind === 'risk' && peer.verdict.risk.direction === 'aboveMax'\n ? t('peerRiskAboveMax')\n : t('peerRiskBelowMin')\n lines.push(`- ${peer.plugin} → ${peer.name}@${peer.range} (${t('checkResolved')}: ${peer.resolved ?? '—'}, ${direction})`)\n }\n lines.push('')\n }\n if ((report.orderConflicts ?? []).length > 0) {\n lines.push(`${t('catOrder')}:`)\n for (const c of report.orderConflicts ?? []) lines.push(`- ${c.name}: ${c.reason}`)\n lines.push('')\n }\n lines.push(t('aiFixScope'))\n lines.push('')\n lines.push(t('aiFixConservative'))\n const prompt = lines.join('\\n')\n\n // Clipboard-first; on any failure (missing API or a rejected promise) show\n // the prompt in a selectable block so the user can still copy it by hand —\n // a bare \"clipboard unavailable\" message left nothing to copy.\n setFixMsg(null)\n setFixFallback(null)\n const fallback = () => setFixFallback(prompt)\n if (typeof navigator.clipboard?.writeText === 'function') {\n navigator.clipboard.writeText(prompt)\n .then(() => setFixMsg(t('aiFixCopied')))\n .catch(fallback)\n } else {\n fallback()\n }\n }\n\n return (\n
\n
\n \n \n {summary.ok ? (anyIssue ? t('checkIssues') : t('diagOkAll')) : t('checkIssues')}\n \n \n {t('catConflict')}: {catConflict}\n \n \n {t('catRisk')}: {catRisk}\n \n \n {t('catWarn')}: {catWarn}\n \n \n {t('catInfo')}: {catInfo}\n \n \n {t('checkMultiVersion')}: {catMulti}\n \n \n {t('catOrder')}: {catOrder}\n \n \n {hasHardIssues && (\n \n )}\n \n {t('checkProfile')}: {report.profile}\n {new Date(report.scannedAt).toLocaleString()}\n
\n {fixMsg !== null &&
{fixMsg}
}\n {fixFallback !== null && (\n
\n

{t('aiFixFail')}

\n e.currentTarget.select()}\n />\n
\n )}\n\n setExplainOpen(o => !o)}>\n

{t('diagExplainText')}

\n
\n
{t('diagTermBundle')}
\n
{t('diagTermEntry')}
\n
{t('diagTermPeer')}
\n
{t('diagTermShadow')}
\n
{t('diagTermOrphan')}
\n
{t('diagTermOrder')}
\n
\n
\n\n 0 ? summary.errors[0] : undefined}\n >\n
\n {summary.errors.map((line, i) => (\n
{line}
\n ))}\n
\n \n\n 0 ? summary.warnings[0] : undefined}\n >\n
\n {summary.warnings.map((line, i) => (\n
{line}
\n ))}\n
\n \n\n \n {t('checkOfficial')} × {report.bundles.filter(b => b.kind === 'official').length}\n {' · '}\n {t('checkCommunity')} × {report.bundles.filter(b => b.kind === 'community').length}\n \n }\n >\n {report.bundles.map((bundle, i) => (\n
\n
\n {i + 1}\n \n {bundle.name}\n \n {bundle.kind === 'official' ? t('checkOfficial') : t('checkCommunity')}\n \n {bundle.error !== null && {bundle.error}}\n {bundle.parseError !== null && {t('checkPatch')}: {bundle.parseError}}\n
\n
\n {t('checkSource')}\n {bundle.source}\n
\n
\n {t('checkEntries')}\n {bundle.entries.length > 0 ? bundle.entries.join(', ') : '—'}\n
\n {bundle.directory !== null && (\n
\n {t('checkDir')}\n {bundle.directory}\n
\n )}\n {bundle.patchPath !== null && (\n
\n {t('checkPatch')}\n {bundle.patchPath}\n
\n )}\n
\n ))}\n \n\n 0 ? `${report.duplicates[0]?.id} × ${report.duplicates[0]?.count}` : undefined}\n >\n
\n {report.duplicates.map(dup => (\n
\n {dup.id}\n × {dup.count}\n {dup.layers.join(' / ')}\n
\n ))}\n
\n \n\n 0\n ? `${peerRisk[0].plugin} → ${peerRisk[0].name}@${peerRisk[0].range}(${t('checkResolved')}: ${peerRisk[0].resolved ?? '—'})`\n : undefined}\n >\n
\n {peerRisk.map((peer, i) => (\n
\n {peer.name}\n {peer.plugin}\n {t('checkRange')}: {peer.range}\n {t('checkResolved')}: {peer.resolved ?? '—'}\n {peerRiskLabel(peer, t)}\n
\n ))}\n
\n \n\n 0\n ? `${peerWarning[0].plugin} → ${peerWarning[0].name}@${peerWarning[0].range}(${peerWarningLabel(peerWarning[0], t)})`\n : undefined}\n >\n
\n {peerWarning.map((peer, i) => (\n
\n {peer.name}\n {peer.plugin}\n {t('checkRange')}: {peer.range}\n {t('checkResolved')}: {peer.resolved ?? '—'}\n {peerWarningLabel(peer, t)}\n
\n ))}\n
\n \n\n 0\n ? `${peerInfo[0].plugin} → ${peerInfo[0].name}@${peerInfo[0].range}`\n : undefined}\n >\n
\n {peerInfo.map((peer, i) => (\n
\n {peer.name}\n {peer.plugin}\n {t('checkRange')}: {peer.range}\n {t('checkResolved')}: {peer.resolved ?? '—'}\n {peer.satisfied === null\n ? {t('checkUnknown')}\n : {t('peerInfoUnverified')}}\n
\n ))}\n
\n \n\n 0\n ? `${peerSatisfied[0].plugin} → ${peerSatisfied[0].name}@${peerSatisfied[0].range}`\n : undefined}\n >\n
\n {peerSatisfied.map((peer, i) => (\n
\n {peer.name}\n {peer.plugin}\n {t('checkRange')}: {peer.range}\n {t('checkResolved')}: {peer.resolved ?? '—'}\n {t('checkSatisfied')}\n
\n ))}\n
\n \n\n 0 ? `${report.multiVersion[0]?.name}: ${report.multiVersion[0]?.versions.join(' / ')}` : undefined}\n >\n
\n {report.multiVersion.map(mv => (\n
\n {mv.name}\n {mv.versions.join(' / ')}\n {mv.hoisted !== null && {t('checkHoisted')}: {mv.hoisted}}\n
\n ))}\n
\n \n\n 0 ? `${report.overrides[0]?.id} ← ${report.overrides[0]?.layer}` : undefined}\n >\n
\n {report.overrides.map((ov, i) => (\n
\n {ov.id}\n \n {ov.layer}\n {t('checkOverridden')}\n {ov.overriddenLayers.join(', ')}\n
\n ))}\n
\n \n\n 0 ? `${report.orphans[0]?.id}(${t(orphanKindLabel(report.orphans[0]?.reason ?? ''))})` : undefined}\n >\n
\n {report.orphans.map((orphan, i) => (\n
\n {t(orphanKindLabel(orphan.reason))}\n {orphan.id}\n {orphan.layer}\n {orphan.reason}\n
\n ))}\n
\n \n\n {/* issue #98 phase 2 (step 1): community-bundle ordering */}\n setOrderOpen(o => !o)}>\n

{t('orderDragHint')}

\n {report.orderConflicts !== undefined && report.orderConflicts.length > 0 && (\n
\n {t('orderConflicts')}\n {report.orderConflicts.map((conflict, i) => (\n
{conflict.name} — {conflict.reason}
\n ))}\n
\n )}\n
\n \n {suggested !== null && suggested.ok === true\n && suggested.order.join('\\u0000') !== communityNames.join('\\u0000')\n && (\n \n )}\n {suggested !== null && suggested.ok === true\n && suggested.order.join('\\u0000') === communityNames.join('\\u0000')\n && {t('orderAlreadyOptimal')}}\n {order.join('\\u0000') !== communityNames.join('\\u0000') && (\n \n )}\n {orderMsg !== null && {orderMsg}}\n {orderErr !== null && {orderErr}}\n
\n {orderDiff !== null && (\n
\n {t('orderDiffHint').replace('{0}', String(orderDiff.overrides)).replace('{1}', String(orderDiff.orphans)).replace('{2}', String(orderDiff.duplicates))}\n
\n )}\n {suggested !== null && suggested.ok === false && (\n
{t('orderSuggestHint')} ⚠ {suggested.cycle.join(' → ')}
\n )}\n {report.duplicateNames !== undefined && report.duplicateNames.length > 0 && (\n
\n {t('duplicateNames')}\n {report.duplicateNames.map((dup, i) => (\n
{dup.name} × {dup.count} — {dup.layers.join(' / ')}
\n ))}\n
\n )}\n {order.length === 0\n ?
\n : (\n
\n {order.map((name, i) => (\n \n \n {i + 1}\n {name}\n \n moveBundle(i, -1)}\n >{t('orderUp')}\n = order.length - 1 || orderBusy}\n onClick={() => moveBundle(i, 1)}\n >{t('orderDown')}\n
\n ))}\n
\n )}\n \n\n {/* issue #98 phase 3: snapshots & rollback */}\n setPresetOpen(o => !o)}>\n {/* The ordering DRAFT, not the last-reported order: a preset must\n capture the arrangement on screen, or saving right after a reorder\n stores the composition the user just moved away from. */}\n \n \n\n setSnapOpen(o => !o)}>\n \n \n
\n )\n}","/**\n * What the browser can see about the market that the server cannot.\n *\n * This exists because of a pattern, not a hypothesis. #293 (\"市场显示空白\")\n * and #384 (\"lightbox cannot be closed\") are both reproducible for their\n * reporters and for nobody else, on clean profiles, across host versions.\n * Both investigations then stalled in the same place: I asked the reporter to\n * open a console and paste the result of an expression. That costs a day per\n * question, asks a non-developer to run code from a stranger, and is the one\n * step where a report goes quiet.\n *\n * Meanwhile the exported log — which the issue template already asks for, and\n * which reporters do send — describes only the server: dependencies, bundle\n * resolution, events. Everything it says was already true of a machine where\n * the bug does not happen. The interesting half was in a page nobody looked\n * at.\n *\n * So the browser answers the questions I have actually had to ask, in the\n * artefact people already know how to produce. Nothing here diagnoses\n * anything on its own; it is evidence, collected before it is needed.\n */\n\n/**\n * How many times this module has been evaluated in this page.\n *\n * Module scope runs once per module instance, so >1 means the market's client\n * bundle was loaded twice — two React copies, two of every module singleton,\n * two portal containers. That is a specific hypothesis for #384: two React\n * roots each attach delegated listeners to their own portal container, and a\n * click lands on whichever container is last in `body` while the handlers\n * live on the other one, so the buttons look present and do nothing.\n *\n * Counted on `window` rather than in a module variable for the obvious\n * reason: a module variable would be duplicated along with everything else\n * and each copy would confidently report 1.\n */\nconst globals = globalThis as typeof globalThis & { __dshmarketClientLoads?: number }\nglobals.__dshmarketClientLoads = (globals.__dshmarketClientLoads ?? 0) + 1\n\n/** Whether `value` looks like a browser environment worth inspecting. */\nconst hasDom = (): boolean => typeof document !== 'undefined' && document.body !== null\n\n/**\n * The browser-side section of the exported log.\n *\n * Deliberately facts, not verdicts. \"portal containers: 2\" is something a\n * reporter can paste without judging it, and something I can act on; \"your\n * bundle is double-loaded\" would be a guess printed in the user's face, and\n * wrong the first time a host legitimately renders two markets.\n * @returns the lines to append, or an empty array outside a browser.\n */\nexport function clientDiagnostics(): string[] {\n if (!hasDom()) return []\n const portals = document.querySelectorAll('[data-dsh-market-portal]')\n const roots = document.querySelectorAll('[data-dsh-market-root]')\n const last = portals.length > 0 ? portals[portals.length - 1] : null\n return [\n // >1 is the double-load signal above. Reported always, so that a normal\n // page proves the number is being read rather than defaulting.\n `client bundle evaluations: ${String(globals.__dshmarketClientLoads ?? 0)}`,\n `market roots in the document: ${String(roots.length)}`,\n // Asked by hand in #384. A container that is not body's last child sits\n // under whatever was appended after it, which is the other way a button\n // can be visible and unclickable.\n `portal containers: ${String(portals.length)}`\n + (last === null ? '' : ` (last one is body's last child: ${String(last === document.body.lastElementChild)})`),\n // Asked by hand in #293: \"blank\" can mean the section never mounted, or\n // mounted and rendered nothing. Those have different causes.\n `plugin cards rendered: ${String(document.querySelectorAll('[data-dsh-market-root] [class*=\"_card\"]').length)}`,\n // The mount point every /dsh-market/* request is resolved against (#345).\n // A surprising value here explains a whole class of \"nothing loads\".\n `document baseURI: ${document.baseURI}`,\n `page URL: ${location.origin}${location.pathname}`,\n `user agent: ${navigator.userAgent}`,\n ]\n}\n","/**\n * The Market settings section: Discover / Themes / Installed tabs over the\n * /dsh-market/* host routes, with install/update/uninstall flows and the\n * pending-restart bookkeeping in sessionStorage.\n */\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, type CSSProperties, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport {\n Button,\n DisclosureRow,\n IconChevronDownOutline14,\n IconChevronLeftOutline14,\n IconChevronRightOutline14,\n IconChevronUpOutline14,\n IconCheckOutline16,\n IconCodeOutline16,\n IconCordisPluginOutline14,\n IconDownloadOutline16,\n IconFolderOpen16,\n IconFullscreenOutline16,\n IconLinkOutline14,\n IconLoadingOutline16,\n IconQuestionOutline14,\n IconRefreshOutline14,\n IconSearchOutline16,\n IconSparkle16,\n IconWarningOutline16,\n Input,\n Menu,\n Modal,\n Pill,\n StateDot,\n Toast,\n Tooltip,\n type MenuEntry,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport css from './Market.module.css'\nimport { CommentsModal } from './CommentsModal.tsx'\nimport { OperationsPanel } from './OperationsPanel.tsx'\nimport { clearSettled, drop, enqueue, patch as patchRecord, recordForUrl } from './operations.ts'\nimport type { OperationRecord } from './operations.ts'\nimport { Diagnostics } from './Diagnostics.tsx'\nimport { clientDiagnostics } from './self-check.ts'\nimport {\n api, avatarColor, entryForDep, githubProxyInUse, githubUrl, groupSwitchState, humanOutput, installedForCatalog, isInstalled, looksTerminal, matchInstalledName, orderedCategories, pluginCategories,\n formatCount, pageItems, pluginName, pluginScreenshotCandidates, pluginScreenshots, rankThemeScreenshots, readSession, safeScreenshots, setGithubProxy, themePlugins as themePluginsOf, themeSwatch, TIME_RANGE_DAYS, visiblePlugins,\n} from './market-data.ts'\nimport type {\nActivationInfo, ActivationState, GistExportResult, InstalledMap, InstalledRepoHints, InstalledRepoIdentities, MarketStatus, Registry, RegistryPlugin,\n ScreenshotCandidate, ScreenshotMeasurement, SharedHostPackageDependencyFinding, SortDir, SortField, ThemeSnapshot, TimeRange, Translate, UpdateStatus,\n} from './market-data.ts'\n\nfunction isHostDependencyFinding(value: unknown): value is SharedHostPackageDependencyFinding {\n if (value === null || typeof value !== 'object') return false\n const finding = value as Partial\n return finding.code === 'shared-host-package-dependency'\n && finding.severity === 'warning'\n && finding.subject?.kind === 'package'\n && typeof finding.subject.name === 'string'\n && finding.evidence?.basis === 'manifest-declaration'\n && typeof finding.evidence?.dependency === 'string'\n && typeof finding.evidence.declaredRange === 'string'\n && finding.evidence.declaredIn === 'dependencies'\n}\n\nconst HOST_DEPENDENCY_PREVIEW_LIMIT = 5\nconst IGNORED_UPDATES_SESSION_KEY = 'dshm-updates-ignored'\n\n/**\n * Read the update reminders dismissed for this host process. The boot id is\n * part of the value rather than the key so sessionStorage never accumulates\n * one orphaned entry per process. Invalid and stale records fail open: an\n * update reminder is safer than silently hiding one we cannot account for.\n */\nfunction ignoredUpdatesForBoot(boot: string): string[] {\n const saved = readSession(IGNORED_UPDATES_SESSION_KEY)\n const valid = saved !== null\n && typeof saved === 'object'\n && !Array.isArray(saved)\n && saved.boot === boot\n && Array.isArray(saved.names)\n && saved.names.every((name: unknown) => typeof name === 'string' && name !== '')\n if (!valid) {\n try { sessionStorage.removeItem(IGNORED_UPDATES_SESSION_KEY) } catch { /* storage unavailable */ }\n return []\n }\n return [...new Set(saved.names as string[])]\n}\n\nfunction HostDependencyDiagnostics({\n findings,\n t,\n}: {\n findings: SharedHostPackageDependencyFinding[]\n t: Translate\n}) {\n if (findings.length === 0) return null\n const preview = findings.slice(0, HOST_DEPENDENCY_PREVIEW_LIMIT)\n const remaining = findings.length - preview.length\n return (\n
\n \n \n
{t('hostDependencyWarning')}
\n {preview.map(finding => (\n \n {finding.subject.name} → {finding.evidence.dependency}@{finding.evidence.declaredRange}\n
\n ))}\n {remaining > 0 && (\n
{t('hostDependencyMore').replace('{0}', String(remaining))}
\n )}\n \n
\n )\n}\n\n/** The state label + dot for one activation result (P0-2). */\nfunction activationMeta(state: ActivationState, t: Translate): { label: string; dot: 'done' | 'warning' | 'error' } {\n if (state === 'live') return { label: t('stateLive'), dot: 'done' }\n if (state === 'restart') return { label: t('stateRestart'), dot: 'warning' }\n if (state === 'inert') return { label: t('stateInert'), dot: 'warning' }\n if (state === 'broken') return { label: t('stateBroken'), dot: 'error' }\n if (state === 'disabled') return { label: t('stateDisabled'), dot: 'warning' }\n return { label: '—', dot: 'warning' }\n}\n\nfunction phaseLabel(phase: NonNullable, t: Translate): string {\n if (phase === 'resolving') return t('phaseResolving')\n if (phase === 'downloading') return t('phaseDownloading')\n if (phase === 'linking') return t('phaseLinking')\n return t('phaseBuilding')\n}\n\n/**\n * Page/page-size state shared by every paged list in this file (Discover,\n * Themes) — each caller owns its OWN instance (their filters are\n * independent, a search in one tab has no business resetting the other's\n * page), but the mechanics (clamp against a shrinking list, reset to page 1\n * when the filters that produced `count` change, scroll back to the top of\n * the shared body on any page move) are one implementation, not two.\n */\nfunction usePagination(count: number, resetDeps: readonly unknown[], scrollToTop: () => void): {\n currentPage: number\n totalPages: number\n pageSize: number\n goToPage: (next: number) => void\n changePageSize: (size: number) => void\n} {\n const [page, setPage] = useState(1)\n const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE)\n // eslint-disable-next-line react-hooks/exhaustive-deps -- resetDeps IS the intended dependency list, supplied by the caller.\n useEffect(() => { setPage(1) }, resetDeps)\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n // Clamp in case the list shrank while the user was on a later page.\n const currentPage = Math.min(page, totalPages)\n const goToPage = (next: number) => {\n setPage(Math.max(1, Math.min(next, totalPages)))\n scrollToTop()\n }\n const changePageSize = (size: number) => {\n setPageSize(size)\n setPage(1)\n scrollToTop()\n }\n return { currentPage, totalPages, pageSize, goToPage, changePageSize }\n}\n\n/**\n * The sort/time-range dropdown (primitives Menu): three independent option\n * groups, ids namespaced so one onSelect routes by prefix. Owns its own\n * open state — a caller wires only the sort VALUES, not the dropdown's UI\n * state, so Discover and Themes can each mount one without threading an\n * extra `filterOpen`/`setFilterOpen` pair through their own state.\n */\nfunction FilterMenu({ sortField, sortDir, timeRange, onSortField, onSortDir, onTimeRange, t }: {\n sortField: SortField\n sortDir: SortDir\n timeRange: TimeRange\n onSortField: (field: SortField) => void\n onSortDir: (dir: SortDir) => void\n onTimeRange: (range: TimeRange) => void\n t: Translate\n}) {\n const [open, setOpen] = useState(false)\n // Direction labels adapt to the field: stars → asc/desc, added → oldest/newest.\n const sortDirLabel = (dir: SortDir): string =>\n sortField === 'added'\n ? dir === 'desc' ? 'sortNewest' : 'sortOldest'\n : dir === 'desc' ? 'sortDesc' : 'sortAsc'\n const items = useMemo(() => [\n { type: 'label', id: 'f-sort', text: t('filterSort') },\n ...SORT_FIELD_OPTIONS.map(opt => ({ id: 'field:' + opt.key, label: t(opt.label) })),\n { type: 'separator', id: 'f-sep1' },\n { type: 'label', id: 'f-dir', text: t('filterDir') },\n ...SORT_DIR_OPTIONS.map(dir => ({ id: 'dir:' + dir, label: t(sortDirLabel(dir)) })),\n { type: 'separator', id: 'f-sep2' },\n { type: 'label', id: 'f-time', text: t('filterTime') },\n ...TIME_OPTIONS.map(opt => ({ id: 'time:' + opt.key, label: t(opt.label) })),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sortDirLabel closes only over sortField, already a dep.\n ], [t, sortField])\n const selectedIds = useMemo(\n () => ['field:' + sortField, 'dir:' + sortDir, 'time:' + timeRange],\n [sortField, sortDir, timeRange])\n const onSelect = (id: string) => {\n if (id.startsWith('field:')) onSortField(id.slice(6) as SortField)\n else if (id.startsWith('dir:')) onSortDir(id.slice(4) as SortDir)\n else if (id.startsWith('time:')) onTimeRange(id.slice(5) as TimeRange)\n }\n return (\n setOpen(false)}\n onSelect={onSelect}\n selectedIds={selectedIds}\n align=\"end\"\n portal\n anchor={(\n : }\n onClick={() => setOpen(o => !o)}\n >{t('filter')}\n )}\n items={items}\n />\n )\n}\n\n/** First/prev/numbered/next/last controls plus a per-page-size menu — one\n * implementation for every paged list, driven entirely by `usePagination`'s\n * return value. Owns its own page-size dropdown open state for the same\n * reason `FilterMenu` owns its own. */\nfunction Pager({ currentPage, totalPages, pageSize, onGoToPage, onChangePageSize, t }: {\n currentPage: number\n totalPages: number\n pageSize: number\n onGoToPage: (page: number) => void\n onChangePageSize: (size: number) => void\n t: Translate\n}) {\n const [sizeOpen, setSizeOpen] = useState(false)\n return (\n
\n
\n {totalPages > 1 && (\n <>\n \n }\n disabled={currentPage === 1}\n onClick={() => onGoToPage(currentPage - 1)}\n >{t('prevPage')}\n {pageItems(currentPage, totalPages).map((item, i) => (\n item === '…'\n ? \n : (\n onGoToPage(item)}\n >{item}\n )\n ))}\n onGoToPage(currentPage + 1)}\n >{t('nextPage')}\n \n {t('pageInfo').replace('{0}', String(currentPage)).replace('{1}', String(totalPages))}\n \n )}\n
\n setSizeOpen(false)}\n onSelect={id => onChangePageSize(Number(id))}\n selectedId={String(pageSize)}\n align=\"end\"\n portal\n anchor={(\n }\n onClick={() => setSizeOpen(o => !o)}\n >{t('perPage') + ' ' + pageSize}\n )}\n items={PAGE_SIZES.map(size => ({ id: String(size), label: String(size) }))}\n />\n
\n )\n}\n\n/**\n * Card avatar: the plugin owner's GitHub avatar (no API, browser-cached),\n * falling back to the initial-letter tile when it can't load.\n */\n/** Inline pass: `code` spans and **bold**, everything else plain text. */\nfunction mdInline(text: string): Array {\n return text.split(/(\\*\\*[^*]+\\*\\*|`[^`]+`)/g).map((part, i) => {\n if (part.startsWith('**') && part.endsWith('**') && part.length > 4) {\n return {part.slice(2, -2)}\n }\n if (part.startsWith('`') && part.endsWith('`') && part.length > 2) {\n return {part.slice(1, -1)}\n }\n return part\n })\n}\n\n/**\n * Release-body markdown, reduced to what a reading dialog needs: headings,\n * bullets, paragraphs, bold, inline code. Every character arrives as a React\n * text child (auto-escaped) — nothing from the repo is ever interpreted as\n * markup, so this stays free of the HTML surface real markdown parsers open.\n */\nfunction renderMarkdown(md: string): Array {\n const out: Array = []\n let bullets: string[] | null = null\n const flushList = (): void => {\n if (bullets === null) return\n const items = bullets\n out.push(
    {items.map((item, i) =>
  • {mdInline(item)}
  • )}
)\n bullets = null\n }\n for (const line of md.split('\\n')) {\n const trimmed = line.trim()\n if (trimmed === '') { flushList(); continue }\n const heading = /^#{1,6}\\s+(.*)$/.exec(trimmed)\n if (heading !== null) {\n flushList()\n out.push(
{mdInline(heading[1])}
)\n continue\n }\n const bullet = /^[-*]\\s+(.*)$/.exec(trimmed)\n if (bullet !== null) {\n ;(bullets ??= []).push(bullet[1])\n continue\n }\n flushList()\n out.push(
{mdInline(line)}
)\n }\n flushList()\n return out\n}\n\nfunction OwnerAvatar({ name, owner }: { name: string; owner: string }) {\n const [failed, setFailed] = useState(false)\n if (failed || owner === '') {\n return (\n
\n {name.replace(/^dsh[-_]/i, '').charAt(0).toUpperCase() || 'P'}\n
\n )\n }\n return (\n setFailed(true)}\n />\n )\n}\n\n/**\n * AppStore-style screenshot strip in the install detail dialog (#61).\n * Curated registry screenshots win; otherwise images are extracted from the\n * repo README. Requests start only once the dialog opens; failures — no\n * README, no images, broken links — degrade to rendering nothing at all.\n */\nfunction ScreenshotStrip({ plugin, onOpen }: { plugin: RegistryPlugin; onOpen: (shots: string[], index: number) => void }) {\n const [shots, setShots] = useState([])\n const [broken, setBroken] = useState([])\n useEffect(() => {\n let live = true\n setShots([])\n setBroken([])\n pluginScreenshots(plugin).then((list) => { if (live) setShots(list) })\n return () => { live = false }\n }, [plugin])\n const visible = shots.filter(src => !broken.includes(src))\n if (visible.length === 0) return null\n return (\n
\n {visible.map((src, i) => (\n onOpen(visible, i)}\n onError={() => setBroken(prev => prev.includes(src) ? prev : prev.concat(src))}\n />\n ))}\n
\n )\n}\n\n/**\n * Advances an index every `intervalMs` while `count > 1` — the shared clock\n * behind both a card's auto-cycling thumbnail and the lightbox. A manual\n * jump (clicking a dot, an arrow, opening on a specific shot) restarts the\n * clock instead of letting it fire again moments later: without that, a\n * deliberate \"go back one\" reads as broken when it auto-advances right past\n * where the user just navigated to.\n *\n * `intervalMs <= 0` disables the timer entirely (no auto-advance at all);\n * manual jumps still work. The lightbox uses this: a full-bleed image needs\n * to stay put until the viewer moves on, so it must never page itself.\n */\nfunction useAutoCarousel(count: number, initial: number, intervalMs = 3500): [number, (i: number) => void] {\n const [index, setIndexState] = useState(initial)\n const [resetTick, setResetTick] = useState(0)\n useEffect(() => {\n if (count <= 1 || intervalMs <= 0) return\n const timer = setInterval(() => { setIndexState(i => (i + 1) % count) }, intervalMs)\n return () => clearInterval(timer)\n }, [count, intervalMs, resetTick])\n const setIndex = (i: number): void => {\n if (count <= 0) return\n setIndexState(((i % count) + count) % count)\n setResetTick(t => t + 1)\n }\n return [index, setIndex]\n}\n\n/**\n * A card thumbnail (or dialog strip image) renders at well under 150px on\n * screen; the curated screenshot behind it can be a full-resolution PNG\n * several hundred KB to a few MB — GitHub's own hosts offer no resized\n * variant, so rendering the original meant downloading full-size images for\n * a strip nobody asked to see full-size. images.weserv.nl resizes\n * server-side (by decoded HEIGHT, `fit=inside` so it never crops, `we=1` so\n * it never upscales something already smaller) before the bytes reach the\n * browser. The lightbox — an explicit \"show me this big\" — still requests\n * the ORIGINAL directly: proxying that one too would add a hop with nothing\n * left to save, and once the thumbnail is genuinely smaller it can no longer\n * share a cache entry with the full-size open anyway.\n */\nfunction thumbUrl(src: string, height: number): string {\n // The resizer stays in every region, including China.\n //\n // It was briefly bypassed there on the assumption that a service in the\n // Netherlands would be one more far-away host in the way. Measured from an\n // unproxied mainland connection, that was wrong twice over: weserv answers\n // in 1.39s, and it answers with 23KB where the original is 41KB. Routing\n // around it would have traded a working request for a bigger one, on a\n // page that makes dozens of them.\n return `https://images.weserv.nl/?url=${encodeURIComponent(src.replace(/^https?:\\/\\//, ''))}&h=${String(height)}&fit=inside&we=1`\n}\n\n/**\n * The owner's GitHub avatar, addressed so the region's proxy can serve it.\n *\n * `github.com/.png` is a redirect to the avatar host, and gh-proxy\n * does not follow it — measured from an unproxied mainland connection, that\n * URL hangs until the client gives up (60s), while naming the avatar host\n * directly through the same proxy answers in 1.07s. So a proxied region\n * addresses the destination itself.\n *\n * The redirect is left in place when there is no proxy: it is the form that\n * has always worked, and this is not the release to change it on a path\n * nobody has reported a problem with.\n */\nfunction avatarUrl(owner: string): string {\n const name = encodeURIComponent(owner)\n return githubProxyInUse() === null\n ? `https://github.com/${name}.png?size=96`\n : githubUrl(`https://avatars.githubusercontent.com/${name}?size=96`)\n}\n\n/**\n * True once the wrapped element has scrolled within `rootMargin` of the\n * viewport. Falls back to true immediately where IntersectionObserver is\n * unavailable (old browsers, jsdom without a stub) — a missing observer\n * should degrade to eager loading, not a permanently empty thumbnail.\n * Native `img loading=\"lazy\"` already defers the network fetch on its own,\n * but its trigger distance isn't ours to tune, and scrolling a 400+ entry\n * catalog queues every off-screen card's request the instant the browser\n * decides to start prefetching — this hook is what lets CardShot not even\n * SET `src` until a card is actually close.\n */\nfunction useNearViewport(rootMargin = '200px'): [(node: T | null) => void, boolean] {\n const [near, setNear] = useState(typeof IntersectionObserver === 'undefined')\n const [node, setNode] = useState(null)\n useEffect(() => {\n if (near || node === null) return\n const obs = new IntersectionObserver((entries) => {\n if (entries.some(entry => entry.isIntersecting)) setNear(true)\n }, { rootMargin })\n obs.observe(node)\n return () => obs.disconnect()\n }, [near, node, rootMargin])\n return [setNode, near]\n}\n\n/**\n * A card's own thumbnail strip — curated screenshots only (#61 supplement):\n * this data already rode along with the catalog fetch that drew the grid,\n * so showing it costs nothing extra. README-scraped fallback images stay\n * dialog-only, where fetching one repo's README on click is a single\n * request instead of one per visible card.\n *\n * Horizontal scroll at each image's own aspect ratio, not an auto-cycling\n * single crop: cropping every shot into one fixed box hid most of a tall\n * screenshot, and cycling on a timer meant the card you were looking at\n * kept changing under you. Scrolling is a gesture the user drives.\n */\n/** Thumbnails per card. The dialog shows every screenshot; a grid of cards\n * pulling six full-size PNGs each is what makes the first paint crawl. */\nconst CARD_SHOT_LIMIT = 3\n\nfunction CardShot({ plugin, onOpen }: { plugin: RegistryPlugin; onOpen: (shots: string[], index: number) => void }) {\n const shots = safeScreenshots(plugin.screenshots)\n const [broken, setBroken] = useState([])\n const visible = shots.filter(src => !broken.includes(src)).slice(0, CARD_SHOT_LIMIT)\n const [setStripRef, near] = useNearViewport()\n if (visible.length === 0) return null\n return (\n
\n {visible.map((src, i) => (\n { e.stopPropagation(); onOpen(visible, i) }}\n onError={() => setBroken(prev => prev.includes(src) ? prev : prev.concat(src))}\n />\n ))}\n
\n )\n}\n\n/**\n * Read dimensions through the same low-resolution, no-upscale route used by\n * card thumbnails. Large originals therefore stay off the wire, while a\n * genuinely tiny image remains tiny and can be rejected by the scorer.\n */\nfunction measureThemeCandidates(candidates: ScreenshotCandidate[]): Promise {\n if (typeof Image === 'undefined') return Promise.resolve([])\n return Promise.all(candidates.map(candidate => new Promise((resolve) => {\n const probe = new Image()\n let settled = false\n const finish = (measurement: ScreenshotMeasurement | null) => {\n if (settled) return\n settled = true\n window.clearTimeout(timer)\n probe.onload = null\n probe.onerror = null\n resolve(measurement)\n }\n const timer = window.setTimeout(() => finish(null), 6_000)\n probe.onload = () => finish({ src: candidate.src, width: probe.naturalWidth, height: probe.naturalHeight })\n probe.onerror = () => finish(null)\n probe.referrerPolicy = 'no-referrer'\n probe.decoding = 'async'\n probe.src = thumbUrl(candidate.src, 240)\n }))).then(results => results.filter((result): result is ScreenshotMeasurement => result !== null))\n}\n\nconst measuredThemePreviewTasks = new Map>()\nconst measuredThemePreviewResults = new Map()\n\n/** Test hook and an explicit boundary for this page-lifetime media cache. */\nexport function resetThemePreviewCache(): void {\n measuredThemePreviewTasks.clear()\n measuredThemePreviewResults.clear()\n}\n\n/** README fetch + geometry probes, shared across search/page remounts. */\nfunction measuredThemePreview(plugin: RegistryPlugin): Promise {\n const cached = measuredThemePreviewTasks.get(plugin.url)\n if (cached !== undefined) return cached\n const task = pluginScreenshotCandidates(plugin).then(async (candidates) => {\n const measurements = await measureThemeCandidates(candidates)\n const ranked = rankThemeScreenshots(candidates, measurements)\n measuredThemePreviewResults.set(plugin.url, ranked)\n return ranked\n }).catch(() => {\n measuredThemePreviewResults.set(plugin.url, [])\n return []\n })\n measuredThemePreviewTasks.set(plugin.url, task)\n return task\n}\n\n/**\n * Themes are chosen visually, so their catalog card gets one stable, large\n * preview instead of the generic plugin card's horizontal thumbnail strip.\n * Curated screenshots keep their declared order. A missing curated set is\n * filled lazily from README only when the card nears the viewport, then\n * ranked by both README semantics and measured image geometry.\n */\nfunction ThemeCover({ plugin, onOpen, t }: {\n plugin: RegistryPlugin\n onOpen: (shots: string[], index: number) => void\n t: Translate\n}) {\n const curated = safeScreenshots(plugin.screenshots)\n const curatedKey = curated.join('\\n')\n const cachedFallback = curated.length === 0 ? measuredThemePreviewResults.get(plugin.url) : undefined\n const [fallback, setFallback] = useState<{ loading: boolean; shots: string[] }>({\n loading: curated.length === 0 && cachedFallback === undefined,\n shots: cachedFallback ?? [],\n })\n const [broken, setBroken] = useState([])\n const [setCoverRef, near] = useNearViewport()\n useEffect(() => {\n setBroken([])\n if (curated.length > 0) {\n setFallback({ loading: false, shots: [] })\n return\n }\n const cached = measuredThemePreviewResults.get(plugin.url)\n if (cached !== undefined) {\n setFallback({ loading: false, shots: cached })\n return\n }\n setFallback({ loading: true, shots: [] })\n if (!near) return\n let live = true\n void measuredThemePreview(plugin).then(shots => { if (live) setFallback({ loading: false, shots }) })\n return () => { live = false }\n // `plugin.url` identifies a card; registry objects are deliberately not a\n // dependency because polling may recreate one without changing its media.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [curatedKey, near, plugin.url])\n const shots = curated.length > 0 ? curated : fallback.shots\n const visible = shots.filter(src => !broken.includes(src))\n const name = pluginName(plugin.name)\n\n if (visible.length === 0) {\n return (\n \n {fallback.loading\n ? \n : }\n {fallback.loading ? t('themePreviewLoading') : t('themePreviewMissing')}\n \n )\n }\n\n const src = visible[0]!\n return (\n onOpen(visible, 0)}\n >\n setBroken(prev => prev.includes(src) ? prev : prev.concat(src))}\n />\n \n \n {t('themePreview')}\n \n {visible.length > 1 && (\n \n {t('themePreviewCount').replace('{0}', String(visible.length))}\n \n )}\n \n )\n}\n\n/**\n * Masonry columns holding items in their input order.\n *\n * Items are dealt alternately (0,2,4… left; 1,3,5… right) rather than split\n * down the middle, so the sort order still reads left-to-right then down —\n * the ranking is the whole point of the sort menu above it. Each column is\n * its own flex stack, so a tall item only pushes down the items beneath IT\n * instead of leaving a hole beside its shorter neighbour.\n *\n * Below the two-up breakpoint the CSS collapses to one column, and dealing\n * alternately would then interleave the list wrongly — so at one column the\n * items stay in a single stack in their original order.\n */\nfunction Masonry({ items, render, columns = 2 }: {\n items: T[]\n render: (item: T) => ReactNode\n columns?: number\n}) {\n const wide = useMediaWide()\n if (!wide || columns < 2) {\n return
{items.map(render)}
\n }\n const buckets: T[][] = Array.from({ length: columns }, () => [])\n items.forEach((item, index) => { buckets[index % columns]!.push(item) })\n return (\n
\n {buckets.map((bucket, index) => (\n
{bucket.map(render)}
\n ))}\n
\n )\n}\n\n/**\n * Whether the layout is at its two-up width. Matches the CSS breakpoint\n * exactly: the column split is decided in JS but rendered by CSS, and the\n * two disagreeing would deal cards into columns the stylesheet has already\n * stacked.\n */\nfunction useMediaWide(): boolean {\n const query = '(min-width: 681px)'\n const subscribe = useCallback((notify: () => void) => {\n if (typeof matchMedia !== 'function') return () => {}\n const list = matchMedia(query)\n list.addEventListener('change', notify)\n return () => list.removeEventListener('change', notify)\n }, [])\n return useSyncExternalStore(\n subscribe,\n () => (typeof matchMedia === 'function' ? matchMedia(query).matches : true),\n // Server/jsdom without matchMedia: assume the two-up layout, which is\n // what the stylesheet defaults to before any media query applies.\n () => true,\n )\n}\n\n/**\n * A card's description, clamped to 5 lines so one wordy entry doesn't blow\n * the two-up grid's row height out for whatever sits beside it — the grid\n * already tolerates SOME height variance by design (`.card`'s `align-self:\n * start`), just not an unbounded one. The toggle only renders when the text\n * actually overflows the clamp: a two-line description has nothing to\n * \"expand\", so no button beats a button that does nothing.\n */\nfunction CardDesc({ text, t }: { text: string; t: Translate }) {\n const ref = useRef(null)\n const [expanded, setExpanded] = useState(false)\n const [canExpand, setCanExpand] = useState(false)\n useLayoutEffect(() => {\n const el = ref.current\n if (el === null) return\n setCanExpand(el.scrollHeight > el.clientHeight + 1)\n }, [text])\n return (\n
\n
{text}
\n {canExpand && (\n setExpanded(e => !e)}\n >\n {expanded ? : }\n \n )}\n
\n )\n}\n\n/**\n * Full-bleed image preview, opened from a card thumbnail or a dialog's\n * screenshot strip. Not the shared Modal primitive: Modal is chrome for a\n * decision (title, description, footer actions); this is just the same\n * already-downloaded image shown bigger — there is no separate \"thumbnail\"\n * vs \"full size\" asset to fetch.\n */\nfunction ScreenshotLightbox({ shots, startIndex, onClose, t }: { shots: string[]; startIndex: number; onClose: () => void; t: Translate }) {\n // Full-bleed previews must not auto-advance: a chart or a screenshot needs\n // to stay readable until the viewer moves on, so the carousel timer is\n // disabled with intervalMs = 0. Arrows, dots, and the keyboard still\n // navigate manually.\n const [index, setIndex] = useAutoCarousel(shots.length, startIndex, 0)\n const host = useMarketPortalHost()\n useEffect(() => {\n // Capture phase + stopPropagation: the Settings dialog underneath is a\n // Modal with its own Escape-to-close handling, also on window/document.\n // Without this, one Escape press closed both layers at once — verified\n // on a real host — because the modal's bubble-phase listener still fired\n // after this one. Capture runs first and this stops it from reaching\n // bubble phase at all, so only the top layer responds to one press.\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') { e.stopPropagation(); onClose() }\n else if (e.key === 'ArrowLeft') { e.stopPropagation(); setIndex(index - 1) }\n else if (e.key === 'ArrowRight') { e.stopPropagation(); setIndex(index + 1) }\n }\n window.addEventListener('keydown', onKey, true)\n return () => window.removeEventListener('keydown', onKey, true)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [index])\n // Into a container this package owns, never into document.body itself.\n //\n // In-tree rendering is not an option: the primitives' own Modal (the\n // settings dialog underneath) portals itself to document.body, so the\n // lightbox rendered in place sat BEHIND it whatever the z-index — a portal\n // only wins a stacking tie against another portal by mounting later.\n // Reported on a real host: \"大的预览图层级不对,现在在弹窗的后面\".\n //\n // But sharing document.body with the host was the other half of a trap.\n // The host's settings dialog and this package are separate React roots,\n // and two roots appending and removing children of the SAME container\n // interleave in an order neither one models. The host's root then calls\n // removeChild for a node this one had already moved, React throws\n // `NotFoundError: The node to be removed is not a child of this node`, the\n // `settings.section` slot catches it, and the whole market panel goes\n // blank (#293 by @Tianhao-1017, #286, #241 — the reporter of #293 traced\n // this to the line, with the stack and a clean-reinstall check).\n //\n // Owning one container fixes that structurally: the host's root sees a\n // single opaque child it never touches, and everything this package\n // mounts or unmounts happens inside it.\n return createPortal(\n
\n {/* A literal \"×\" rather than IconCloseOutline16: the primitives\n package's own Modal uses that icon at runtime, but this package\n version's public type surface doesn't resolve it — `tsc` reports\n \"no exported member\" even though icons/index.d.ts declares it.\n Not worth a type-check suppression for one close glyph. */}\n \n \"\" e.stopPropagation()} />\n {shots.length > 1 && (\n <>\n { e.stopPropagation(); setIndex(index - 1) }}\n >\n { e.stopPropagation(); setIndex(index + 1) }}\n >\n
e.stopPropagation()}>\n {shots.map((src, i) => (\n setIndex(i)}\n />\n ))}\n
\n \n )}\n
,\n host,\n )\n}\n\n/**\n * The one DOM node this package portals into, created on first use and kept\n * for the life of the page.\n *\n * Created imperatively rather than rendered, and never removed: the point is\n * that `document.body`'s child list stops being shared state between two\n * React roots. A container that came and went would put the same churn back\n * into body, just less often — and \"less often\" is what made this bug\n * intermittent and hard to believe in the first place.\n *\n * Re-appended on every open so it stays last among body's children. That is\n * what keeps the lightbox above the host's own portalled dialog, which is\n * why the portal exists at all; moving a node we own is not something the\n * host's root tracks, so it cannot disturb it.\n */\nlet portalHost: HTMLElement | null = null\n\nfunction marketPortalHost(): HTMLElement {\n if (portalHost === null) {\n portalHost = document.createElement('div')\n // Named so anyone inspecting the DOM, or a future host wanting to give\n // plugins a real portal slot, can see who owns it.\n portalHost.setAttribute('data-dsh-market-portal', '')\n }\n return portalHost\n}\n\n/**\n * Move the container to the end of `document.body`, which is what keeps this\n * package's layers above the host's own portalled dialog.\n *\n * In a layout effect, NOT during render. `createPortal` needs the element\n * while rendering, but appending it does not belong there: React may start a\n * render, abandon it and start again, so a mutation in the render body runs\n * for passes that never commit — and this particular mutation reorders\n * `document.body`, the one container this package shares with the host's\n * separate React root. That is the same shared-child-list hazard #293 was\n * about, just arrived at from the other side. Committing it in an effect\n * means it happens once, after React is done, in the order React expects.\n */\nfunction useMarketPortalHost(): HTMLElement {\n const host = marketPortalHost()\n useLayoutEffect(() => {\n // appendChild on an existing child MOVES it to the end — the stacking\n // guarantee, refreshed on open without ever creating a second container.\n document.body.appendChild(host)\n }, [host])\n return host\n}\n\n/** Test hook: the container is module state and outlives a component unmount. */\nexport function resetMarketPortalHost(): void {\n portalHost?.remove()\n portalHost = null\n}\n\n/**\n * Official-style market glyph: the shared block-grid brand mark converted to\n * the official monochrome icon form (16×16, fill=\"currentColor\") so it\n * follows the active theme. Mirrors the settings-nav glyph used for the\n * \"market\" section id.\n */\nfunction MarketLogo({ size = 16, style, animated = false }: { size?: number; style?: CSSProperties; animated?: boolean }) {\n return (\n \n \n \n \n \n \n \n \n \n \n \n {/* The block being plugged in: OUTSIDE the grid's empty corner, offset\n (+1.28, -1.27) and tilted 9deg, exactly as in assets/logo.svg. The\n earlier icon sat it neatly in the empty slot, which reads as one\n crooked tile rather than a block arriving — the whole idea of the\n mark, and the reason it no longer matched the GitHub logo. */}\n \n \n )\n}\n\n/**\n * GitHub mark beside catalog card titles (#256, #365). Catalog intake in\n * awesome-dsh-plugin rejects any entry whose `url` is not\n * `https://github.com/owner/repo` (scripts/lib/entries.mjs), so this renders\n * unconditionally — not a bet that today's snapshot happens to be all\n * GitHub. The generic outbound arrow did not say so until hover. This rides\n * the title's own line — no second link, no extra row.\n */\nfunction GithubRepoMark({ size = 12, className }: { size?: number; className?: string }) {\n return (\n \n \n \n )\n}\n\n/**\n * Module-scope caches so re-entering the section renders instantly instead\n * of refetching and rebuilding from a spinner (#30 by @StarsTom). Module\n * state survives section switches; a background refetch keeps it current.\n */\nlet cachedRegistry: Registry | null = null\nlet cachedInstalled: InstalledMap | null = null\nlet cachedRepoIdentities: InstalledRepoIdentities | null = null\nlet cachedRepoHints: InstalledRepoHints | null = null\n\n/** Discover grid page-size choices — the catalog grows daily, so cap each page. */\nconst PAGE_SIZES = [24, 48, 96]\nconst DEFAULT_PAGE_SIZE = 24\nconst WEBDAV_STORAGE_KEY = 'dshm-webdav'\n\nfunction savedWebdav(): { url: string; username: string; password: string; auto: boolean } {\n try {\n const value = JSON.parse(localStorage.getItem(WEBDAV_STORAGE_KEY) ?? '{}') as Record\n return {\n url: typeof value.url === 'string' ? value.url : '',\n username: typeof value.username === 'string' ? value.username : '',\n // The password never persists in the browser: plugins run same-origin\n // with dshmarket, so a stored password would be readable by any plugin\n // client on this host and become the weakest credential in the profile\n // (review #63). It lives in server config / memory only.\n password: '',\n auto: value.auto === true,\n }\n } catch {\n return { url: '', username: '', password: '', auto: false }\n }\n}\n\nfunction backupDependencies(value: unknown): InstalledMap {\n if (value === null || typeof value !== 'object') throw new Error('invalid backup')\n const backup = value as { format?: unknown; version?: unknown; files?: unknown }\n if (backup.format !== 'dsh-profile-backup' || backup.version !== 0.2) throw new Error('unsupported backup format')\n const files = backup.files\n if (!Array.isArray(files)) throw new Error('unsupported backup format')\n const manifest = files.find(file => file !== null && typeof file === 'object' && (file as { path?: unknown }).path === 'package.json') as { json?: unknown } | undefined\n if (manifest?.json === null || typeof manifest?.json !== 'object' || Array.isArray(manifest.json)) throw new Error('backup package.json is invalid')\n const dependencies = (manifest.json as { dependencies?: unknown }).dependencies\n if (dependencies === null || typeof dependencies !== 'object' || Array.isArray(dependencies)) return {}\n if (!Object.values(dependencies).every(spec => typeof spec === 'string')) throw new Error('backup dependencies are invalid')\n return dependencies as InstalledMap\n}\n\nfunction installedRepoIdentities(value: unknown): InstalledRepoIdentities {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return {}\n const identities: InstalledRepoIdentities = {}\n for (const [name, ids] of Object.entries(value)) {\n if (!Array.isArray(ids)) continue\n const strings = ids.filter((id): id is string => typeof id === 'string')\n if (strings.length > 0) identities[name] = strings\n }\n return identities\n}\n\nfunction installedRepoHints(value: unknown): InstalledRepoHints {\n return installedRepoIdentities(value)\n}\n\nfunction installedMap(value: unknown): InstalledMap {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return {}\n const installed: InstalledMap = {}\n for (const [name, spec] of Object.entries(value)) {\n if (typeof spec === 'string') installed[name] = spec\n }\n return installed\n}\n\nfunction sameInstalledMap(left: InstalledMap, right: InstalledMap): boolean {\n const names = Object.keys(left)\n return names.length === Object.keys(right).length && names.every(name => left[name] === right[name])\n}\n\n/** Sort field choices in the filter panel. */\nconst SORT_FIELD_OPTIONS: ReadonlyArray<{ key: SortField; label: string }> = [\n { key: 'downloads', label: 'sortDownloads' },\n { key: 'stars', label: 'sortStars' },\n { key: 'added', label: 'sortAdded' },\n]\n\n/** Sort direction choices in the filter panel (labels depend on the field). */\nconst SORT_DIR_OPTIONS: ReadonlyArray = ['desc', 'asc']\n\n/** Published-within choices in the filter panel. */\nconst TIME_OPTIONS: ReadonlyArray<{ key: TimeRange; label: string }> = [\n { key: 'all', label: 'timeAll' },\n { key: 'day', label: 'timeDay' },\n { key: 'week', label: 'timeWeek' },\n { key: 'month', label: 'timeMonth' },\n { key: 'quarter', label: 'timeQuarter' },\n { key: 'year', label: 'timeYear' },\n]\n\n\nexport interface MarketSectionProps {\n t: Translate\n locale: {\n subscribe(callback: () => void): () => void\n getSnapshot(): { active: string }\n }\n theme: { setTheme(id: string): void }\n themeStore: {\n subscribe(callback: () => void): () => void\n getSnapshot(): ThemeSnapshot | null\n }\n /** Optional host-provided destination: `discover:` or `installed:`. */\n preferredSubsectionId?: string\n}\n\nexport function MarketSection(props: MarketSectionProps) {\n const t = props.t\n const initialWebdav = useMemo(savedWebdav, [])\n const localeSnap = useSyncExternalStore(\n cb => props.locale.subscribe(cb),\n () => props.locale.getSnapshot(),\n )\n const lang = String(localeSnap.active).toLowerCase().startsWith('zh') ? 'zh' : 'en'\n // null when the composition has no theme service — the Themes tab hides.\n const themeSnap = useSyncExternalStore(\n props.themeStore.subscribe,\n props.themeStore.getSnapshot,\n )\n const [data, setData] = useState(cachedRegistry)\n const [loadError, setLoadError] = useState(null)\n const [installed, setInstalledState] = useState(cachedInstalled ?? {})\n const setInstalled = useCallback((value: InstalledMap) => { cachedInstalled = value; setInstalledState(value) }, [])\n const [repoIdentities, setRepoIdentitiesState] = useState(cachedRepoIdentities ?? {})\n const setRepoIdentities = useCallback((value: InstalledRepoIdentities) => {\n cachedRepoIdentities = value\n setRepoIdentitiesState(value)\n }, [])\n const [repoHints, setRepoHintsState] = useState(cachedRepoHints ?? {})\n const setRepoHints = useCallback((value: InstalledRepoHints) => {\n cachedRepoHints = value\n setRepoHintsState(value)\n }, [])\n const [installedFiles, setInstalledFiles] = useState([])\n const [skins, setSkins] = useState([])\n const [tab, setTab] = useState(() => {\n const saved = sessionStorage.getItem('dshm-tab')\n if (saved !== null) sessionStorage.removeItem('dshm-tab')\n return saved || 'discover'\n })\n const [q, setQ] = useState('')\n /** Per-tab searches stay independent: discover / themes / installed. */\n const [qThemes, setQThemes] = useState('')\n const [qInstalled, setQInstalled] = useState('')\n const [cat, setCat] = useState('all')\n // FLAQ Desktop supplies this for onboarding/feature navigation; upstream dsh web omits it, so ordinary web opens intentionally leave this effect idle.\n useEffect(() => {\n const target = props.preferredSubsectionId\n if (target === undefined) return\n const separator = target.indexOf(':')\n const kind = separator === -1 ? target : target.slice(0, separator)\n const value = separator === -1 ? '' : target.slice(separator + 1)\n if (kind === 'installed') {\n setTab('installed')\n setQInstalled(value)\n } else if (kind === 'discover') {\n setTab('discover')\n setCat('all')\n setQ(value)\n }\n }, [props.preferredSubsectionId])\n const [confirming, setConfirming] = useState(null)\n /** The plugin whose comment thread is open, or null. */\n const [commentsFor, setCommentsFor] = useState(null)\n /** A rejected install and the installed plugins it clashed with, one entry\n * per owner as grouped by the host. */\n interface ConflictNotice {\n plugin: RegistryPlugin\n groups: Array<{ owner: string; ids: string[] }>\n }\n /**\n * Every mutating operation the user started. Records outlive the card that\n * started them, so paginating or searching cannot take a pending decision\n * off screen.\n */\n const [records, setRecords] = useState([])\n const recordSeq = useRef(0)\n /** The synthetic install task rebuilt from dshm-pending after a remount. */\n const recoveredInstall = useRef<{ id: string; url: string; name?: string } | null>(null)\n /** The synthetic task rebuilt from dshm-updating after this section remounts. */\n const recoveredUpdateRecordId = useRef(null)\n /** Raised by the card marker, so \"查看详情\" lands on the record itself. */\n const [operationsOpen, setOperationsOpen] = useState(false)\n const openOperations = useCallback(() => setOperationsOpen(true), [])\n /**\n * Two plugins can ship under one name from different authors, so a roster\n * row that shows only the package name cannot tell the user which of their\n * plugins a swap would uninstall. Resolve through the catalog for the\n * author and avatar a card would show, and fall back to the bare name for\n * anything installed outside it.\n */\n const describePlugin = useCallback((name: string) => {\n const entry = data?.plugins.find(plugin => plugin.npm === name || plugin.name === name)\n if (entry === undefined) return { title: name }\n return {\n title: pluginName(entry.name),\n author: entry.owner === '' ? undefined : entry.owner,\n avatar: ,\n }\n }, [data])\n /** Ids are sequential rather than random so a replayed session is stable. */\n const nextRecordId = useCallback(() => {\n recordSeq.current += 1\n return `op-${String(recordSeq.current)}`\n }, [])\n const [replacing, setReplacing] = useState(false)\n /** Shared by every screenshot source (card thumbnail, dialog strip). */\n const [lightbox, setLightbox] = useState<{ shots: string[]; index: number } | null>(null)\n const openLightbox = (shots: string[], index: number): void => setLightbox({ shots, index })\n const [themesFullscreen, setThemesFullscreen] = useState(false)\n const [busyUrl, setBusyUrl] = useState(null)\n /** Consecutive idle polls with a pending install that never landed (#32). */\n const idleStrikes = useRef(0)\n /** Same idle-strike bookkeeping for an update whose response was lost. */\n const updateIdleStrikes = useRef(0)\n const [doneUrls, setDoneUrls] = useState([])\n const [installError, setInstallError] = useState(null)\n /** The notes payload the server answers with, verbatim (see /changelog). */\n type NoteRelease = { tag: string | null; name: string | null; publishedAt: string | null; url: string | null; body: string }\n type NoteCommit = { sha: string; message: string; date: string | null }\n interface UpdateNotes {\n kind: 'release' | 'commits' | 'npm' | 'none'\n release?: NoteRelease\n commits?: { items: NoteCommit[]; found: boolean }\n npmTimes?: Array<{ version: string; date: string }>\n }\n type ResolvedNotes =\n | { kind: 'release'; release: NoteRelease }\n | { kind: 'commits'; commits: { items: NoteCommit[]; found: boolean } }\n | { kind: 'npm'; npmTimes: Array<{ version: string; date: string }> }\n | { kind: 'none' }\n interface CompatibilityNotice {\n code: 'soft-incompatible'\n risks: Array<{ plugin: string; peer: string; range: string; resolved: string; direction: string }>\n /** Cross-layer loader-name collisions this operation introduced (#230). */\n shadowedNames?: Array<{ name: string; layers: string[]; count: number }>\n /** Client bundles that no longer parse after the operation (#222). */\n brokenBundles?: Array<{ name: string; reason: string }>\n rollbackId?: string\n rollbackUnavailable?: string\n }\n const [compatibilityNotice, setCompatibilityNotice] = useState(null)\n const [rollingBack, setRollingBack] = useState(false)\n /** Log export lifecycle for visible feedback (#84): idle → busy → done/fail. */\n const [exportState, setExportState] = useState<'idle' | 'busy' | 'done' | 'fail'>('idle')\n\n /**\n * Programmatic log download with explicit feedback (#84) — the plain\n * `` gave no sign anything happened, and the error banner's\n * \"export the log\" wording pointed at text that was not clickable at all.\n * Success/failure surface as a primitives Toast (body portal, no layout\n * impact) instead of inline text.\n */\n const doExportLog = useCallback(() => {\n setExportState('busy')\n fetch(api('/dsh-market/logs'))\n .then(async (res) => {\n if (!res.ok) throw new Error(`HTTP ${String(res.status)}`)\n // The server half describes the server. Everything it reports was\n // already true on machines where the reported bug does not happen,\n // which is why #293 and #384 both stalled on \"please open a console\n // and paste this\". The browser appends what only it can see — see\n // self-check.ts. Done here rather than sent to the route so this\n // adds no endpoint, no request body, and no new trust boundary.\n const serverText = await res.text()\n const browser = clientDiagnostics()\n const blob = new Blob(\n [serverText, ...(browser.length > 0 ? ['## browser\\n', browser.join('\\n'), '\\n'] : [])],\n { type: 'text/plain;charset=utf-8' },\n )\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = 'dsh-market-log.txt'\n document.body.appendChild(anchor)\n anchor.click()\n anchor.remove()\n URL.revokeObjectURL(url)\n setExportState('done')\n })\n .catch(() => setExportState('fail'))\n }, [])\n /** Stable onDone for the export Toast — a fresh closure per render would\n * reset the Toast's auto-dismiss timer on every parent re-render. */\n const exportToastDone = useCallback(() => setExportState('idle'), [])\n const [updates, setUpdates] = useState>({})\n /** Update reminders dismissed for this host boot. The Installed tab still\n * shows these plugins and their update actions; only proactive prompts use\n * this set. */\n const [ignoredUpdateNames, setIgnoredUpdateNames] = useState([])\n const [updatingName, setUpdatingName] = useState(null)\n /** Update-notes dialog (#294): which row opened it, and what it resolved to. */\n const [notesFor, setNotesFor] = useState<{ name: string; current: string | null; latest: string | null; repoUrl: string | null } | null>(null)\n const [updateNotes, setUpdateNotes] = useState(null)\n const [notesState, setNotesState] = useState<'loading' | 'ready' | 'fail'>('loading')\n // Plugin blocked by pnpm's fresh-release safety wait; arms the update-now button.\n const [staleName, setStaleName] = useState(null)\n // Local link:/file: restore: the red banner asks before swapping to the catalog.\n const [restoreName, setRestoreName] = useState(null)\n\n /** Determinate percent parsed from pnpm's Progress line, when available. */\n const [progressPct, setProgressPct] = useState(null)\n /**\n * Blocked build scripts from the last install or update: enables\n * approve-and-retry (#6; updates in #69). Exactly one of `plugin`\n * (retry installs it) / `updateName` (retry re-runs the update) is set.\n */\n const [buildsSkipped, setBuildsSkipped] = useState<{ plugin?: RegistryPlugin; updateName?: string; names: string[]; restore?: boolean } | null>(null)\n const [updatingAll, setUpdatingAll] = useState(false)\n const [updatedNames, setUpdatedNames] = useState([])\n const [hotUrls, setHotUrls] = useState([])\n const [hotNames, setHotNames] = useState([])\n const [progressLine, setProgressLine] = useState(null)\n /** Per-package activation states from /dsh-market/installed + operations. */\n const [activations, setActivations] = useState>({})\n /** #60: persisted disable list + custom groups, straight from /installed. */\n const [disabledNames, setDisabledNames] = useState([])\n /** The user's own note per plugin (#347): package name → text. */\n const [notes, setNotes] = useState>({})\n /** Rows the user asked to show the AUTHOR's description on, despite a note. */\n const [showTheirs, setShowTheirs] = useState([])\n /** The row whose note is being edited, and the text in the box. */\n const [notingName, setNotingName] = useState(null)\n const [noteDraft, setNoteDraft] = useState('')\n /** The disable set as of the first load; null until it arrives. */\n const loadedDisabled = useRef | null>(null)\n /**\n * Patch-layer flags (port of dsh-plugin-hub): packages whose bundle rows\n * the user patch layer disables / force-enables. The UI treats them as the\n * real switch state so hand-edited cordis.patch.yml toggles are visible.\n */\n const [patchDisabledNames, setPatchDisabledNames] = useState([])\n const [groups, setGroups] = useState>({})\n const [groupOrder, setGroupOrder] = useState([])\n /** Installed-tab sub-view: flat list or groups (All-plugins was removed —\n * it duplicated the Discover tab). */\n const [installedView, setInstalledView] = useState<'list' | 'groups'>('list')\n const [togglingName, setTogglingName] = useState(null)\n // Group editor state (create / rename / delete / assign).\n const [creatingGroup, setCreatingGroup] = useState(false)\n const [newGroupName, setNewGroupName] = useState('')\n const [renamingGroup, setRenamingGroup] = useState(null)\n const [renamingValue, setRenamingValue] = useState('')\n const [deletingGroup, setDeletingGroup] = useState(null)\n /** Open group picker: which group and whether it adds plugins or themes. */\n const [addPanel, setAddPanel] = useState<{ group: string; kind: 'plugin' | 'theme' } | null>(null)\n const [assignFor, setAssignFor] = useState(null)\n const [assignTarget, setAssignTarget] = useState('')\n /** Structured progress from pnpm ndjson (P1-6). */\n const [progressPhase, setProgressPhase] = useState(null)\n const [progressCurrent, setProgressCurrent] = useState(null)\n const [progressDone, setProgressDone] = useState(0)\n const [cancelling, setCancelling] = useState(false)\n /** Server-side operation lock from /dsh-market/status (#91). */\n const [hostBusy, setHostBusy] = useState(false)\n /**\n * The market's own version, shown beside the heading. Most bug reports\n * arrive as a photo of the screen, and without a version in frame the\n * first reply always has to ask which one it was.\n */\n const [version, setVersion] = useState(null)\n /** Non-live activation results from the last operation, shown as a banner. */\n const [activationWarnings, setActivationWarnings] = useState<{ name: string; info: ActivationInfo }[]>([])\n const [hostDependencyFindings, setHostDependencyFindings] = useState([])\n /** Plugin name awaiting uninstall confirmation (Modal). */\n const [removeConfirm, setRemoveConfirm] = useState(null)\n const [removingName, setRemovingName] = useState(null)\n const [removedCount, setRemovedCount] = useState(0)\n /** Toggles whose live fiber did not follow the switch — restart to apply. */\n const [toggleRestart, setToggleRestart] = useState(0)\n /** Last completed toggle, shown as a toast (#299). The switch and the row\n * tag already say the new state, but both live in a row the user may have\n * scrolled past — a mis-click there goes unnoticed. The toast is fixed on\n * screen, so it is the part that actually catches an accident. */\n const [toggled, setToggled] = useState<{ name: string; enabled: boolean } | null>(null)\n const toggledDone = useCallback(() => setToggled(null), [])\n /**\n * Dismissal of the host-reported restart notice, keyed to the current boot\n * so it reappears after a restart that did not happen and after any new\n * change. sessionStorage, not local: closing the tab is a fresh start.\n */\n const [restartNoticeDismissed, setRestartNoticeDismissed] = useState(false)\n /** Client-part plugins toggled this session — their UI needs a refresh. */\n const [refreshNames, setRefreshNames] = useState([])\n const [envReady, setEnvReady] = useState(true)\n const [envFixing, setEnvFixing] = useState(false)\n const [envFailed, setEnvFailed] = useState(false)\n const [bootId, setBootId] = useState(null)\n /** One-click restart (#14 by @ysyyhhh): server capability + in-flight state. */\n const [restartEnabled, setRestartEnabled] = useState(false)\n /** Supervisor the host detected around itself, when it named one (#229). */\n const [supervisor, setSupervisor] = useState(null)\n /** Debugger latch when one-click restart must not kill the host (#447). */\n const [debuggerLatch, setDebuggerLatch] = useState(null)\n const [restarting, setRestarting] = useState(false)\n const [showTop, setShowTop] = useState(false)\n const [backupBusy, setBackupBusy] = useState(false)\n const [backupMessage, setBackupMessage] = useState(null)\n const [backupRestored, setBackupRestored] = useState(false)\n const [pendingBackup, setPendingBackup] = useState(null)\n const [pendingDependencies, setPendingDependencies] = useState({})\n const [webdavUrl, setWebdavUrl] = useState(initialWebdav.url)\n const [webdavUser, setWebdavUser] = useState(initialWebdav.username)\n const [webdavPassword, setWebdavPassword] = useState(initialWebdav.password)\n const [autoBackup, setAutoBackup] = useState(initialWebdav.auto)\n /** GitHub token — session memory only, never written to any storage. */\n const [gistToken, setGistToken] = useState('')\n /** Gist id — persisted across reloads (non-sensitive: the Gist itself is private). */\n const [gistId, setGistId] = useState(() => {\n try { return localStorage.getItem('dshm-gist-id') ?? '' } catch { return '' }\n })\n /** Export mode: 'update' PATCHes the Gist in the field, 'create' makes a new one. */\n const [gistMode, setGistMode] = useState<'update' | 'create'>(() => {\n try { return localStorage.getItem('dshm-gist-id') ? 'update' : 'create' } catch { return 'create' }\n })\n const [gistBusy, setGistBusy] = useState(false)\n const [gistMessage, setGistMessage] = useState(null)\n const [gistOk, setGistOk] = useState(false)\n const [gistResult, setGistResult] = useState(null)\n /** Export picker: open state, selected plugin names, include-config flag. */\n const [exportOpen, setExportOpen] = useState(false)\n const [exportSelection, setExportSelection] = useState>(new Set())\n const [exportIncludeConfig, setExportIncludeConfig] = useState(false)\n /** Export failure shown INSIDE the picker so it is never hidden behind it. */\n const [exportError, setExportError] = useState(null)\n /** Bundle-only plugin names from /dsh-market/installed (picker list). */\n const [installedBundles, setInstalledBundles] = useState([])\n const bodyRef = useRef(null)\n /** Hidden file input behind the Import button (a Button can't host an ). */\n const fileInputRef = useRef(null)\n const [sortField, setSortField] = useState('downloads')\n const [sortDir, setSortDir] = useState('desc')\n const [timeRange, setTimeRange] = useState('all')\n const [catsOpen, setCatsOpen] = useState(false)\n /** Themes tab: independent from Discover's sort/time state above — a\n * search or sort choice in one tab has no business resetting the other. */\n const [themeSortField, setThemeSortField] = useState('downloads')\n const [themeSortDir, setThemeSortDir] = useState('desc')\n const [themeTimeRange, setThemeTimeRange] = useState('all')\n /** WebDAV provider-preset dropdown (primitives Menu). */\n const [presetOpen, setPresetOpen] = useState(false)\n /** Install-command disclosure inside the confirm dialog. */\n const [cmdOpen, setCmdOpen] = useState(false)\n /** Per-row \"why is it not live\" disclosure (installed tab). */\n const [whyOpen, setWhyOpen] = useState(null)\n /** Restore-confirm dialog (replaces window.confirm). */\n const [restoreConfirmOpen, setRestoreConfirmOpen] = useState(false)\n /** Plugins that failed to install during a restore (replaces window.alert). */\n const [restoreErrors, setRestoreErrors] = useState([])\n // How many category pills fit in the two collapsed rows (measured once —\n // the settings panel width is fixed); null = measuring render with all\n // pills clamped, then slice so the chevron flows inline after the last one.\n const [visibleCats, setVisibleCats] = useState(null)\n /** Same idea as `visibleCats`, but how many fit in a single row — used to\n * shrink an expanded (2+ row) category list while the sticky header is\n * pinned during scroll, distinct from the two-row collapsed default. */\n const [visibleCatsOneRow, setVisibleCatsOneRow] = useState(null)\n const catsWrapRef = useRef(null)\n // Whether the sticky header is currently pinned to the top of the scroll\n // area. Tracked via a sentinel just above it rather than a scrollTop\n // threshold: the threshold would have to hard-code the header's offset\n // (padding, sticky `top`), which drifts silently whenever that CSS\n // changes. The sentinel just reports what's actually true on screen.\n const [catsStuck, setCatsStuck] = useState(false)\n const [catsSentinel, setCatsSentinel] = useState(null)\n\n const refreshInstalled = useCallback((force?: boolean) => {\n fetch(api('/dsh-market/installed'), { cache: 'no-store' })\n .then(res => res.json())\n .then(body => {\n setInstalled(body.installed || {})\n setRepoIdentities(installedRepoIdentities(body.repoIdentities))\n setRepoHints(installedRepoHints(body.repoHints))\n setInstalledFiles(Array.isArray(body.present) ? body.present : Object.keys(body.installed || {}))\n setSkins(body.live || [])\n if (Array.isArray(body.disabled)) {\n setDisabledNames(body.disabled)\n // The switch positions this page was BUILT with. A toggle away from\n // them needs a refresh; a toggle back to them does not, and the\n // banner has to be able to say so (#340).\n if (loadedDisabled.current === null) loadedDisabled.current = new Set(body.disabled as string[])\n }\n if (body.notes !== null && typeof body.notes === 'object' && !Array.isArray(body.notes)) {\n setNotes(body.notes as Record)\n }\n if (Array.isArray(body.patchDisabled)) setPatchDisabledNames(body.patchDisabled)\n if (body.groups && typeof body.groups === 'object') setGroups(body.groups)\n if (Array.isArray(body.groupOrder)) setGroupOrder(body.groupOrder)\n setInstalledBundles(Array.isArray(body.bundles) ? body.bundles.filter((name: unknown): name is string => typeof name === 'string') : [])\n if (body.activation && typeof body.activation === 'object') setActivations(body.activation)\n const findings = body.diagnostics?.schema === 'dsh-market/diagnostics/v1'\n && Array.isArray(body.diagnostics.findings)\n ? body.diagnostics.findings.filter(isHostDependencyFinding)\n : []\n setHostDependencyFindings(findings)\n })\n .catch(() => {})\n fetch(api('/dsh-market/updates') + (force === true ? '?force=1' : ''), { cache: 'no-store' })\n .then(res => res.json())\n .then(body => setUpdates(body.updates || {}))\n .catch(() => {})\n }, [])\n\n /** Active Bundles count as installed in Discover without becoming package-manager targets. */\n const catalogInstalled = useMemo(\n () => installedForCatalog(installed, installedBundles),\n [installed, installedBundles],\n )\n /** Lookup set for the persisted disable list (#60). */\n const disabledSet = useMemo(() => new Set(disabledNames), [disabledNames])\n /** Effective switch state: market disable list ∪ user-patch-layer disables. */\n const effectiveDisabledSet = useMemo(\n () => new Set([...disabledNames, ...patchDisabledNames]),\n [disabledNames, patchDisabledNames],\n )\n\n useEffect(() => {\n if (tab !== 'themes' && themesFullscreen) setThemesFullscreen(false)\n }, [tab, themesFullscreen])\n\n useEffect(() => {\n if (!themesFullscreen || lightbox !== null) return\n const onKey = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return\n event.stopPropagation()\n setThemesFullscreen(false)\n }\n window.addEventListener('keydown', onKey, true)\n return () => window.removeEventListener('keydown', onKey, true)\n }, [lightbox, themesFullscreen])\n\n const loadCatalog = useCallback(() => {\n setLoadError(null)\n return fetch(api('/dsh-market/registry'), { cache: 'no-store' })\n .then(async (res) => {\n const body = (await res.json().catch(() => ({}))) as { registry?: Registry; error?: string }\n if (!res.ok) throw new Error(typeof body.error === 'string' ? body.error : `HTTP ${String(res.status)}`)\n return body\n })\n .then((body) => {\n if (body.registry === undefined) throw new Error('the catalog response carried no data')\n cachedRegistry = body.registry\n setData(body.registry)\n setLoadError(null)\n })\n // Report WHY. An unreachable catalog used to be answered with a\n // bundled copy, so \"cannot reach the registry\" and \"the catalog is\n // smaller today\" looked identical on screen — and the second reading\n // is the one users reached.\n .catch((error: unknown) => { setLoadError(error instanceof Error ? error.message : String(error)) })\n }, [])\n\n useEffect(() => {\n void loadCatalog()\n fetch(api('/dsh-market/status'), { cache: 'no-store' })\n .then(res => res.json())\n .then(status => {\n setEnvReady(status.pnpm !== false)\n // Applied before anything renders a github.com URL. The catalog this\n // page draws from is a larger request through the same server, so it\n // lands later; and if it ever did not, the status poll re-renders\n // within seconds and the images correct themselves.\n setGithubProxy(typeof status.githubProxy === 'string' ? status.githubProxy : null)\n if (typeof status.boot === 'string') {\n setBootId(status.boot)\n setIgnoredUpdateNames(ignoredUpdatesForBoot(status.boot))\n // A dismissal only silences the notice for the boot it was made\n // in: if the user dismissed instead of restarting, the next boot\n // (or a stale dismissal from a previous one) shows it again.\n try {\n setRestartNoticeDismissed(sessionStorage.getItem('dshm-restart-dismissed') === status.boot)\n } catch { /* storage unavailable */ }\n }\n setRestartEnabled(status.restart === true)\n setSupervisor(typeof status.supervisor === 'string' ? status.supervisor : null)\n setDebuggerLatch(typeof status.debugger === 'string' ? status.debugger : null)\n if (typeof status.version === 'string' && status.version !== '') setVersion(status.version)\n })\n .catch(() => {})\n refreshInstalled()\n }, [refreshInstalled, loadCatalog])\n\n // Pending-restart flags survive tab switches and page reloads, scoped to\n // one host process: a different boot id means the restart happened and the\n // stale banner must not resurrect.\n useEffect(() => {\n if (bootId === null) return\n const saved = readSession('dshm-restart')\n if (saved === null) return\n if (saved.boot !== bootId) {\n sessionStorage.removeItem('dshm-restart')\n return\n }\n if (Array.isArray(saved.doneUrls) && saved.doneUrls.length > 0) setDoneUrls(saved.doneUrls)\n if (Array.isArray(saved.updated) && saved.updated.length > 0) setUpdatedNames(saved.updated)\n if (typeof saved.removed === 'number' && saved.removed > 0) setRemovedCount(saved.removed)\n if (typeof saved.toggled === 'number' && saved.toggled > 0) setToggleRestart(saved.toggled)\n }, [bootId])\n\n useEffect(() => {\n if (bootId === null) return\n if (doneUrls.length === 0 && updatedNames.length === 0 && removedCount === 0 && toggleRestart === 0) {\n // Nothing pending: drop any stale entry (e.g. a hot mount cleared the\n // only doneUrl) so a same-boot remount cannot resurrect the banner (#73).\n sessionStorage.removeItem('dshm-restart')\n return\n }\n sessionStorage.setItem('dshm-restart', JSON.stringify({\n boot: bootId,\n doneUrls,\n updated: updatedNames,\n removed: removedCount,\n toggled: toggleRestart,\n }))\n }, [bootId, doneUrls, updatedNames, removedCount, toggleRestart])\n\n const fixEnv = useCallback(() => {\n setEnvFixing(true)\n setEnvFailed(false)\n fetch(api('/dsh-market/setup-pnpm'), { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' })\n .then(res => res.json())\n .then(body => {\n if (body.ok) {\n setEnvReady(true)\n } else {\n setEnvFailed(true)\n if (typeof body.error === 'string') setInstallError(body.error)\n }\n })\n .catch(() => setEnvFailed(true))\n .finally(() => setEnvFixing(false))\n }, [])\n\n // Recover an install whose HTTP response was lost (page navigated away or\n // the connection dropped): the pending marker survives in sessionStorage and\n // the poll below converges the button state from the host's ground truth.\n useEffect(() => {\n const pending = readSession('dshm-pending')\n if (pending !== null && typeof pending.url === 'string') {\n setBusyUrl(pending.url)\n recoveredInstall.current = {\n id: `recovered-install:${pending.url}`,\n url: pending.url,\n ...(typeof pending.name === 'string' && pending.name !== '' ? { name: pending.name } : {}),\n }\n }\n // Same recovery for an update in flight: closing the config page unmounts\n // this section and drops `updatingName` with it, so the running row's\n // progress vanished on reopen. The marker restores the row and the poll\n // below converges it from the host's ground truth.\n const updating = readSession('dshm-updating')\n if (updating !== null && typeof updating.name === 'string' && updating.name !== '') {\n setUpdatingName(updating.name)\n const id = `recovered-update:${updating.name}`\n recoveredUpdateRecordId.current = id\n setRecords(list => list.some(record =>\n record.kind === 'update' && record.name === updating.name && record.state === 'running')\n ? list\n : enqueue(list, { id, kind: 'update', name: updating.name, state: 'running' }))\n }\n }, [])\n\n // New markers carry the name and recover immediately. Older markers only\n // carried the URL, so wait for the catalog and resolve the same task from it.\n useEffect(() => {\n const recovered = recoveredInstall.current\n if (recovered === null) return\n const name = recovered.name ?? data?.plugins.find(plugin => plugin.url === recovered.url)?.name\n if (name === undefined) return\n recovered.name = name\n setRecords(list => list.some(record => record.id === recovered.id)\n ? list\n : enqueue(list, {\n id: recovered.id, kind: 'install', name, url: recovered.url, state: 'running',\n }))\n }, [data])\n\n useEffect(() => {\n if (busyUrl === null && updatingName === null) {\n // `hostBusy` is sampled by the progress poll. A normal update response\n // can settle the local operation before the next poll observes the\n // route lock released, leaving the restart button disabled until this\n // section remounts (#440). With no tracked install/update left, discard\n // that stale sample; the guarded restart route still handles the small\n // post-response lock-release window with its existing 409 retry.\n setHostBusy(false)\n setProgressLine(null)\n setProgressPhase(null)\n setProgressCurrent(null)\n setProgressDone(0)\n setCancelling(false)\n return\n }\n const timer = setInterval(() => {\n fetch(api('/dsh-market/status'), { cache: 'no-store' })\n .then(res => res.json())\n .then(status => {\n setHostBusy(status.busy === true)\n setDebuggerLatch(typeof status.debugger === 'string' ? status.debugger : null)\n if (status.active) {\n setCancelling(status.cancelling === true)\n if (status.phase !== null && status.phase !== undefined) {\n // Structured pnpm progress: stage + current package + count.\n setProgressPhase(status.phase)\n setProgressCurrent(status.currentPackage ?? null)\n setProgressDone(status.done ?? 0)\n setProgressLine(null)\n if (typeof status.size === 'number' && status.size > 0 && typeof status.downloaded === 'number') {\n setProgressPct(Math.max(4, Math.min(96, Math.round(status.downloaded / status.size * 100))))\n }\n } else {\n setProgressLine((status.lastLine || '…') + ' (' + status.seconds + 's)')\n setProgressPhase(null)\n setProgressCurrent(null)\n setProgressDone(0)\n const m = /resolved (\\d+), reused (\\d+), downloaded (\\d+), added (\\d+)/.exec(status.lastLine || '')\n if (m !== null && Number(m[1]) > 0) {\n const done = Number(m[2]) + Number(m[3]) + Number(m[4])\n setProgressPct(Math.max(4, Math.min(96, Math.round(done / Number(m[1]) * 100))))\n }\n }\n } else {\n setProgressLine(null)\n setProgressPct(null)\n setProgressPhase(null)\n setProgressCurrent(null)\n setProgressDone(0)\n setCancelling(false)\n const statusInstalled = installedMap(status.installed)\n if (!sameInstalledMap(installed, statusInstalled)) refreshInstalled()\n const pending = readSession('dshm-pending')\n // status.busy (#91): pnpm exited but the install route still\n // holds the operation lock (validation, hot-mount). Neither\n // declare the install done nor count an idle strike yet — a\n // premature banner here invited a restart click into a 409.\n if (pending !== null && busyUrl !== null && status.busy !== true) {\n const nowInstalled = data !== null && data.plugins.some(p =>\n p.url === busyUrl && isInstalled(p, statusInstalled, repoIdentities, data.plugins, repoHints))\n if (nowInstalled) {\n idleStrikes.current = 0\n sessionStorage.removeItem('dshm-pending')\n const recovered = recoveredInstall.current\n if (recovered !== null) {\n setRecords(list => drop(list, recovered.id))\n recoveredInstall.current = null\n }\n setDoneUrls(urls => urls.includes(busyUrl) ? urls : urls.concat(busyUrl))\n setBusyUrl(null)\n } else if (++idleStrikes.current >= 2) {\n // Host is idle and the plugin never landed: the install died\n // (e.g. exit 127) with its response lost. Without this the\n // button says \"installing\" forever — across reloads (#32).\n idleStrikes.current = 0\n sessionStorage.removeItem('dshm-pending')\n const recovered = recoveredInstall.current\n if (recovered !== null) {\n setRecords(list => drop(list, recovered.id))\n recoveredInstall.current = null\n }\n setBusyUrl(null)\n setInstallError(t('installFail') + ' — ' + t('exportLog'))\n }\n }\n // An update whose response was lost — the page was closed mid-run\n // and reopened via the dshm-updating marker — converges the same\n // way. Once the host reports the operation fully settled (pnpm\n // exited AND the mutation lock released), hand the running row\n // back to the refreshed listing instead of showing \"updating\"\n // forever. Two idle polls guard the brief window before the host\n // has actually started the command.\n if (updatingName !== null && status.busy !== true) {\n if (++updateIdleStrikes.current >= 2) {\n updateIdleStrikes.current = 0\n sessionStorage.removeItem('dshm-updating')\n const recoveredId = recoveredUpdateRecordId.current\n if (recoveredId !== null) {\n setRecords(list => drop(list, recoveredId))\n recoveredUpdateRecordId.current = null\n }\n setUpdatingName(null)\n refreshInstalled()\n }\n } else {\n updateIdleStrikes.current = 0\n }\n }\n })\n .catch(() => {})\n }, 2000)\n return () => clearInterval(timer)\n }, [busyUrl, updatingName, data, installed, repoIdentities, repoHints, refreshInstalled])\n\n const scrollToTop = () => {\n const el = bodyRef.current\n if (el) {\n // jsdom (tests) lacks Element.scrollTo — fall back to the assignment.\n if (typeof el.scrollTo === 'function') el.scrollTo({ top: 0, behavior: 'smooth' })\n else el.scrollTop = 0\n }\n }\n\n // The .body scroller is shared across top tabs AND in-tab list replacements\n // (Discover/Themes category, search, sort; Installed search and list/groups).\n // Leaving scrollTop in place opens the next list mid-page — or, when it is\n // shorter, at its clamped bottom. Instant (not the smooth scrollToTop used\n // for pagination) so the jump happens before paint.\n useLayoutEffect(() => {\n const el = bodyRef.current\n if (el !== null) el.scrollTop = 0\n setShowTop(false)\n }, [tab, q, cat, sortField, sortDir, timeRange, qThemes, themeSortField, themeSortDir, themeTimeRange, qInstalled, installedView])\n\n const plugins = useMemo(\n () => (data === null ? [] : visiblePlugins(data.plugins, {\n category: cat, query: q, lang, categories: data.categories,\n sort: `${sortField}-${sortDir}`,\n sinceDays: timeRange === 'all' ? undefined : TIME_RANGE_DAYS[timeRange],\n })),\n [data, q, cat, lang, sortField, sortDir, timeRange])\n const { currentPage, totalPages, pageSize, goToPage, changePageSize } =\n usePagination(plugins.length, [q, cat, sortField, sortDir, timeRange], scrollToTop)\n const pagePlugins = plugins.slice((currentPage - 1) * pageSize, currentPage * pageSize)\n\n const themePlugins = useMemo(\n () => (data === null ? [] : visiblePlugins(data.plugins, {\n category: 'theme', query: qThemes, lang, categories: data.categories,\n sort: `${themeSortField}-${themeSortDir}`,\n sinceDays: themeTimeRange === 'all' ? undefined : TIME_RANGE_DAYS[themeTimeRange],\n })),\n [data, qThemes, lang, themeSortField, themeSortDir, themeTimeRange])\n const themePagination = usePagination(\n themePlugins.length, [qThemes, themeSortField, themeSortDir, themeTimeRange], scrollToTop)\n const themePagePlugins = themePlugins.slice(\n (themePagination.currentPage - 1) * themePagination.pageSize, themePagination.currentPage * themePagination.pageSize)\n\n /** Download a host endpoint as a file — primitives Button can't be an .\n * Prefers the server's Content-Disposition filename (e.g. the timestamped\n * backup export) and falls back to the caller's name. */\n const downloadFile = useCallback((url: string, filename: string) => {\n fetch(url)\n .then(res => {\n if (!res.ok) throw new Error('HTTP ' + res.status)\n const disposition = res.headers.get('content-disposition')\n if (disposition !== null) {\n const match = /filename=\"?([^\";]+)\"?/.exec(disposition)\n if (match !== null && match[1] !== undefined && match[1] !== '') filename = match[1]\n }\n return res.blob()\n })\n .then(blob => {\n const a = document.createElement('a')\n a.href = URL.createObjectURL(blob)\n a.download = filename\n a.click()\n setTimeout(() => URL.revokeObjectURL(a.href), 2000)\n })\n .catch(error => setInstallError(String(error)))\n }, [])\n\n const doRollback = useCallback((rollbackId: string) => {\n setRollingBack(true)\n setInstallError(null)\n fetch(api('/dsh-market/rollback'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ rollbackId }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n setCompatibilityNotice(null)\n refreshInstalled()\n } else {\n setInstallError(String(body.error || body.detail || 'rollback failed'))\n }\n })\n .catch(error => setInstallError(String(error)))\n .finally(() => setRollingBack(false))\n }, [refreshInstalled])\n\n const compatibilitySummary = (risks: CompatibilityNotice['risks']): string => {\n if (risks.length === 0) return ''\n const first = risks[0]\n return `${first.plugin}: ${first.peer} ${first.range} vs ${first.resolved}`\n }\n\n /** Which name now resolves from two layers, and which layers those are. */\n const shadowSummary = (entries: NonNullable): string => {\n if (entries.length === 0) return ''\n const first = entries[0]\n const rest = entries.length > 1 ? ` (+${entries.length - 1})` : ''\n return `${first.name} — ${first.layers.join(' / ')}${rest}`\n }\n\n const doInstall = useCallback((plugin: RegistryPlugin) => {\n setBuildsSkipped(null)\n setConfirming(null)\n setInstallError(null)\n setActivationWarnings([])\n setBusyUrl(plugin.url)\n // One record per attempt. A retry appends rather than reusing the old\n // one, so the card resolves to the newest and its Install button returns.\n const recordId = nextRecordId()\n setRecords(list => enqueue(list, {\n id: recordId, kind: 'install', name: plugin.name, url: plugin.url, state: 'running',\n }))\n sessionStorage.setItem('dshm-pending', JSON.stringify({ url: plugin.url, name: plugin.name }))\n fetch(api('/dsh-market/install'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ url: plugin.url }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n setBusyUrl(null)\n sessionStorage.removeItem('dshm-pending')\n if (status === 200 && body.ok && body.hot && pluginCategories(plugin).includes('theme')) {\n // Themes auto-activate on install; reload straight into the Themes\n // tab so the new look is on screen immediately.\n sessionStorage.setItem('dshm-toast', JSON.stringify([plugin.name]))\n sessionStorage.setItem('dshm-tab', 'themes')\n location.reload()\n return\n }\n if (body.cancelled === true) {\n // User-cancelled: quiet reset, nothing to report.\n setRecords(list => drop(list, recordId))\n refreshInstalled()\n if (body.partial === true) setInstallError(t('partialNote'))\n return\n }\n if (status === 200 && body.ok) {\n sessionStorage.setItem('dshm-tab', 'installed')\n if (body.activation && typeof body.activation === 'object') {\n setActivations(prev => ({ ...prev, ...body.activation }))\n const warns = Object.entries(body.activation as Record)\n .filter(([, info]) => info.state !== 'live' && info.state !== 'missing')\n .map(([name, info]) => ({ name, info }))\n setActivationWarnings(warns)\n }\n if (body.hot) {\n // The status-poll recovery path may have already counted this URL\n // as pending-restart before the install response confirmed a hot\n // mount; a hot plugin must not stay in doneUrls (#73).\n setDoneUrls(urls => urls.filter(url => url !== plugin.url))\n setHotUrls(urls => urls.includes(plugin.url) ? urls : urls.concat(plugin.url))\n setHotNames(names => names.includes(plugin.name) ? names : names.concat(plugin.name))\n } else {\n setDoneUrls(urls => urls.includes(plugin.url) ? urls : urls.concat(plugin.url))\n }\n if (body.compatibility?.code === 'soft-incompatible') {\n setCompatibilityNotice(body.compatibility as CompatibilityNotice)\n }\n // `warned` keeps the ✓: the plugin IS installed, so calling a\n // compatibility risk a failure would misreport what happened.\n setRecords(list => patchRecord(list, recordId, body.compatibility?.code === 'soft-incompatible'\n ? { state: 'warned', reason: t('compatRiskBanner') }\n : { state: 'done', needsRefresh: body.hot !== true }))\n refreshInstalled()\n } else {\n if (status === 409) {\n const busyReason = body.agentsBusy === true\n ? t('agentBusyInstall') + (Array.isArray(body.runningAgents) && body.runningAgents.length > 0 ? ` (${body.runningAgents.join(', ')})` : '')\n : t('busyWait')\n setRecords(list => patchRecord(list, recordId, { state: 'failed', reason: busyReason }))\n setOperationsOpen(true)\n return\n }\n // A clash is not a failure to report and forget: the host already\n // reverted it, so what remains is a decision. `input` keeps the\n // record in the panel until the user answers it.\n if (Array.isArray(body.conflictGroups) && body.conflictGroups.length > 0) {\n setRecords(list => patchRecord(list, recordId, {\n state: 'input', conflicts: body.conflictGroups as ConflictNotice['groups'],\n }))\n // Raise the panel for anything that needs an answer. A red dot on\n // a closed panel is not a report; out of sight is out of mind.\n setOperationsOpen(true)\n return\n }\n const blocked = Array.isArray(body.ignoredBuilds) ? body.ignoredBuilds.map(String) : []\n if (blocked.length > 0) setBuildsSkipped({ plugin, names: blocked })\n const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)\n const orphans = Array.isArray(body.orphanBundles) ? body.orphanBundles.map(String) : []\n const failure = text(body.error) || humanOutput([text(body.stderr), text(body.stdout)].filter(Boolean).join('\\n')) || ('exit ' + body.exitCode)\n // The profile will not boot as it stands (#339). Said FIRST, because\n // it outranks whatever else went wrong: a plugin that failed to\n // install is recoverable, a profile that cannot start is not — and\n // the user would otherwise meet it as a Node stack trace after the\n // next restart, with nothing linking it to this operation.\n // A stale catalog entry (#346) is said before pnpm's own wording,\n // which for that failure reads like the user broke something.\n const staleEntry = typeof body.staleEntry === 'string' ? body.staleEntry : null\n const detail = [\n orphans.length > 0 ? `${t('orphanBundle')} ${orphans.join(', ')}` : null,\n staleEntry,\n failure,\n ].filter(Boolean).join('\\n')\n // Carry the blocked names onto the record too: the panel is where\n // this failure is read, so it is where the one-click way out has to\n // be (#314).\n setRecords(list => patchRecord(list, recordId, {\n state: 'failed', reason: detail.trim().slice(-600),\n ...(blocked.length > 0 ? { blockedBuilds: blocked } : {}),\n }))\n setOperationsOpen(true)\n }\n })\n .catch(() => {\n // #100: a long install can outlive its HTTP response (loopback\n // stacks and proxies reset idle connections) while pnpm keeps\n // working server-side — declaring failure here produced a false\n // \"install failed, export the log\" with an EMPTY log (the route\n // only logs when it finishes), followed by the plugin quietly\n // appearing minutes later. Keep dshm-pending and the busy button\n // instead, and let the status poll decide: its recovery path marks\n // success once the plugin lands (busy-aware since #91) and strikes\n // out genuinely dead installs (#32).\n })\n }, [nextRecordId, refreshInstalled, t])\n\n /**\n * Resolve a loader-id clash the only way one profile allows: uninstall the\n * plugins holding the ids, then retry the install. Sequential because each\n * route takes the host's mutation lock, so a parallel burst would 409.\n *\n * A failure part-way leaves plugins already gone. Nothing reinstalls them\n * automatically (a rollback would itself be an install that can fail), so\n * the message names them — reporting only \"failed\" would leave the user\n * guessing which of their plugins survived.\n */\n const doReplace = useCallback(async (record: OperationRecord, plugin: RegistryPlugin) => {\n setInstallError(null)\n setReplacing(true)\n const removed: string[] = []\n try {\n for (const group of record.conflicts ?? []) {\n const response = await fetch(api('/dsh-market/uninstall'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name: group.owner }),\n })\n const body = await response.json() as { ok?: boolean; error?: unknown; stderr?: unknown }\n if (response.status !== 200 || body.ok !== true) {\n const text = (v: unknown) => typeof v === 'string' ? v : v == null ? '' : JSON.stringify(v)\n const detail = (text(body.error) || humanOutput(text(body.stderr)) || 'error').trim().slice(-400)\n const reason = removed.length === 0\n ? `${t('installFail')}: ${group.owner} — ${detail}`\n : `${t('conflictReplaceFailed')} ${removed.join(', ')} — ${detail}`\n setRecords(list => patchRecord(list, record.id, { state: 'failed', conflicts: undefined, reason }))\n setOperationsOpen(true)\n refreshInstalled()\n return\n }\n removed.push(group.owner)\n }\n } finally {\n setReplacing(false)\n }\n // The clash record is done with; the retry opens its own, so the card\n // resolves to the new attempt rather than the answered decision.\n setRecords(list => drop(list, record.id))\n refreshInstalled()\n doInstall(plugin)\n }, [doInstall, refreshInstalled, t])\n\n /**\n * Answer a clash. `keep` is not a no-op to skip: it is the user declining\n * the install, so the record retires rather than lingering as unanswered.\n */\n const resolveConflict = useCallback((record: OperationRecord, choice: 'keep' | 'swap') => {\n if (choice === 'keep') {\n setRecords(list => patchRecord(list, record.id, {\n state: 'failed', conflicts: undefined, reason: t('conflictDeclined'),\n }))\n return\n }\n const plugin = data?.plugins.find(candidate => candidate.url === record.url)\n if (plugin === undefined) return\n void doReplace(record, plugin)\n }, [data, doReplace, t])\n\n /**\n * Restart the host and reload once the boot id changes (#14 by @ysyyhhh).\n * The 202 races the process's SIGTERM, so network errors on the initial\n * request are expected and treated as \"restart under way\".\n */\n const doRestart = useCallback(() => {\n if (bootId === null || restarting) return\n const previousBoot = bootId\n setRestarting(true)\n setInstallError(null)\n const awaitNewBoot = () => {\n const deadline = Date.now() + 60000\n const poll = () => {\n fetch(api('/dsh-market/status'), { cache: 'no-store' })\n .then(res => res.json())\n .then((next) => {\n if (typeof next.boot === 'string' && next.boot !== previousBoot) {\n location.reload()\n return\n }\n retry()\n })\n .catch(retry)\n }\n const retry = () => {\n if (Date.now() > deadline) {\n setRestarting(false)\n setInstallError(t('restartTimeout'))\n return\n }\n setTimeout(poll, 1500)\n }\n poll()\n }\n const requestRestart = (attemptsLeft: number) => {\n fetch(api('/dsh-market/restart'), { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 202 && body.ok === true) {\n awaitNewBoot()\n return\n }\n // 409 = the install route still holds the operation lock for its\n // post-processing (#91) — a short quiet retry beats surfacing\n // \"cannot restart while a plugin operation is running\" to a user\n // who just followed our own banner.\n if (status === 409 && attemptsLeft > 0) {\n setTimeout(() => requestRestart(attemptsLeft - 1), 1500)\n return\n }\n setRestarting(false)\n setInstallError(t('restartFail') + ': ' + String(body.error || ('HTTP ' + String(status))))\n })\n .catch(awaitNewBoot) // the host may die mid-response; keep polling\n }\n requestRestart(10)\n }, [bootId, restarting, t])\n\n /** Cancel the running plugin command (#6 by @qichuang321). */\n const doCancel = useCallback(() => {\n fetch(api('/dsh-market/cancel'), { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' })\n .catch(() => {})\n }, [])\n\n const doUpdate = useCallback((name: string, force = false, restore = false) => {\n setInstallError(null)\n setActivationWarnings([])\n // Only THIS row's stale marker is cleared. \"Update all\" walks the list\n // calling straight into here, so an unconditional reset meant every\n // earlier release-age failure lost its retry button and only the last\n // one kept it — the rest failed silently with no way forward (#255).\n setStaleName(prev => (prev === name ? null : prev))\n setRestoreName(prev => (prev === name ? null : prev))\n setUpdatingName(name)\n updateIdleStrikes.current = 0\n // Mirror the install flow's dshm-pending marker: closing the config page\n // unmounts this section and drops `updatingName`, so the running row's\n // progress was lost on reopen. The marker survives the unmount and lets a\n // reopen restore the row while the status poll converges the outcome.\n sessionStorage.setItem('dshm-updating', JSON.stringify({ name }))\n // The Tasks panel exists to answer \"what is running right now\", and an\n // update is one of the things that runs. `OperationKind` has carried\n // 'update' since the panel was written; only the enqueue was missing, so\n // \"update all\" left the panel empty while several plugins were mid-flight\n // (#295 by @sanyecao88). One record per attempt, like the install flow.\n const updateRecordId = nextRecordId()\n setRecords(list => enqueue(list, { id: updateRecordId, kind: 'update', name, state: 'running' }))\n return fetch(api('/dsh-market/update'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name, ...(force ? { force: true } : {}), ...(restore ? { restore: true } : {}) }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n // A response means the host settled the request (even a 4xx/5xx), so\n // the running row can hand back now. Only a lost response keeps the\n // marker + row for the poll to converge.\n sessionStorage.removeItem('dshm-updating')\n setUpdatingName(null)\n if (body.cancelled === true) {\n setRecords(list => drop(list, updateRecordId))\n refreshInstalled()\n if (body.partial === true) setInstallError(t('partialNote'))\n return\n }\n if (status === 200 && body.ok) {\n setRecords(list => patchRecord(list, updateRecordId, { state: 'done' }))\n setUpdatedNames(names => names.concat(name))\n if (body.activation && typeof body.activation === 'object') {\n setActivations(prev => ({ ...prev, ...body.activation }))\n }\n if (body.compatibility?.code === 'soft-incompatible') {\n setCompatibilityNotice(body.compatibility as CompatibilityNotice)\n }\n refreshInstalled()\n } else {\n if (status === 409) {\n if (body.agentsBusy === true) {\n const running = Array.isArray(body.runningAgents) && body.runningAgents.length > 0 ? ` (${body.runningAgents.join(', ')})` : ''\n setRecords(list => patchRecord(list, updateRecordId, { state: 'failed', reason: t('agentBusyUpdate') + running }))\n setInstallError(t('agentBusyUpdate') + running)\n return\n }\n setRecords(list => patchRecord(list, updateRecordId, { state: 'failed', reason: t('busyWait') }))\n setInstallError(t('busyWait'))\n return\n }\n if (body.stale === true) setStaleName(name)\n // Blocked build scripts during an update (#69): same\n // approve-and-retry banner as the install flow, retrying the update.\n if (Array.isArray(body.ignoredBuilds) && body.ignoredBuilds.length > 0) {\n setBuildsSkipped({ updateName: name, names: body.ignoredBuilds.map(String), restore })\n }\n const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)\n const orphans = Array.isArray(body.orphanBundles) ? body.orphanBundles.map(String) : []\n const failure = text(body.error) || humanOutput([text(body.stderr), text(body.stdout)].filter(Boolean).join('\\n')) || ('exit ' + body.exitCode)\n // The profile will not boot as it stands (#339). Said FIRST, because\n // it outranks whatever else went wrong: a plugin that failed to\n // install is recoverable, a profile that cannot start is not — and\n // the user would otherwise meet it as a Node stack trace after the\n // next restart, with nothing linking it to this operation.\n // A stale catalog entry (#346) is said before pnpm's own wording,\n // which for that failure reads like the user broke something.\n const staleEntry = typeof body.staleEntry === 'string' ? body.staleEntry : null\n const detail = [\n orphans.length > 0 ? `${t('orphanBundle')} ${orphans.join(', ')}` : null,\n staleEntry,\n failure,\n ].filter(Boolean).join('\\n')\n setRecords(list => patchRecord(list, updateRecordId, { state: 'failed', reason: detail.trim().slice(-600) }))\n setInstallError((restore ? t('restoreFail') : t('updateFail')) + ': ' + name + ' — ' + detail.trim().slice(-600))\n }\n })\n .catch(() => {\n // A lost response does not mean the update stopped (the route holds\n // its reply until pnpm finishes, #100): keep the marker AND the\n // running row, and let the status poll converge the outcome instead\n // of declaring a false failure — mirroring the install flow's catch.\n })\n }, [refreshInstalled, t])\n\n const askRestore = useCallback((name: string) => {\n const spec = installed[name]\n const entry = data === null || spec === undefined\n ? undefined\n : entryForDep(data.plugins, name, String(spec), repoIdentities[name], repoHints[name])\n setStaleName(null)\n if (entry === undefined) {\n setRestoreName(null)\n setInstallError(t('restoreNoCatalog'))\n return\n }\n setRestoreName(name)\n setInstallError(t('restoreHint'))\n }, [data, installed, repoHints, repoIdentities, t])\n\n /** Open the update-notes dialog and start its fetch. Lazy: the request only\n exists while a user is actually looking at one plugin's notes, and\n closing the dialog abandons the render — the server side caches the\n payload, so reopening is cheap. */\n const openNotes = useCallback((name: string, current: string | null, latest: string | null, repoUrl: string | null) => {\n setNotesFor({ name, current, latest, repoUrl })\n setUpdateNotes(null)\n setNotesState('loading')\n fetch(`${api('/dsh-market/changelog')}?name=${encodeURIComponent(name)}`)\n .then(res => res.json())\n .then(body => { setUpdateNotes(body as ResolvedNotes); setNotesState('ready') })\n .catch(() => setNotesState('fail'))\n }, [])\n\n const doUseSkin = useCallback((name: string) => { setInstallError(null)\n fetch(api('/dsh-market/use-skin'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n sessionStorage.setItem('dshm-toast', JSON.stringify([name]))\n sessionStorage.setItem('dshm-toast-mode', 'theme')\n sessionStorage.setItem('dshm-tab', 'themes')\n location.reload()\n } else {\n setInstallError(String(body.error || 'failed'))\n }\n })\n .catch(error => setInstallError(String(error)))\n }, [])\n\n /**\n * Forget a pending page-refresh for a plugin that is no longer here.\n *\n * The banner counts what the page has not caught up with. Install then\n * uninstall and the page is level again — there is nothing left to load —\n * but both sets were append-only, so it kept asking for a refresh that\n * would show nothing (#340). It conflated \"something needs doing\" with\n * \"something happened in this session\".\n */\n /** Write (or clear, when empty) this plugin's note. */\n const saveNote = useCallback((name: string, text: string) => {\n setNotingName(null)\n fetch(api('/dsh-market/note'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name, text }),\n })\n .then(res => res.json())\n .then((body) => {\n if (body.ok && body.notes !== null && typeof body.notes === 'object') {\n setNotes(body.notes as Record)\n } else setInstallError(String(body.error || 'note failed'))\n })\n .catch(error => setInstallError(String(error)))\n }, [])\n\n const clearPendingRefresh = useCallback((name: string) => {\n setHotNames(names => names.filter(entry => entry !== name))\n setRefreshNames(names => names.filter(entry => entry !== name))\n }, [])\n\n const doUninstall = useCallback((name: string) => {\n setRemoveConfirm(null)\n setInstallError(null)\n setActivationWarnings([])\n setRemovingName(name)\n return fetch(api('/dsh-market/uninstall'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n if (!body.hot) setRemovedCount(n => n + 1)\n // A client-part plugin stays injected until a page reload — the same\n // pending-refresh banner as enable/disable tells the user to reload,\n // instead of silently leaving the uninstalled plugin's UI running.\n //\n // But only when it was live in THIS page. A plugin installed during\n // this session was never injected: the banner was asking the user to\n // reload in order to GET it, so undoing the install nets to zero and\n // the banner must go (#340 — \"it was reporting session history, not\n // pending work\"). Being already pending is exactly what distinguishes\n // the two, and the server cannot see it: `refresh` says the package\n // HAD a client part, not that this page ever loaded it.\n const neverLoadedHere = hotNames.includes(name) || refreshNames.includes(name)\n if (body.refresh === true && !neverLoadedHere) {\n setRefreshNames(names => names.includes(name) ? names : names.concat(name))\n } else clearPendingRefresh(name)\n refreshInstalled()\n } else {\n if (body.cancelled === true) {\n refreshInstalled()\n if (body.partial === true) setInstallError(t('partialNote'))\n return\n }\n // Half-uninstall reconcile: the package is gone and the server has\n // already converged the manifest to disk truth. Refresh so the card\n // leaves the list instead of luring the user into a retry that\n // would 400 on \"not installed\"; the note separates the outcome\n // (removed, profile synced) from the process (pnpm errored).\n if (body.reconciled === true) {\n if (!body.hot) setRemovedCount(n => n + 1)\n clearPendingRefresh(name)\n refreshInstalled()\n setInstallError(t('reconciledNote'))\n return\n }\n const text = (v: unknown) => typeof v === 'string' ? v : (v && typeof (v as any).text === 'string') ? (v as any).text : v == null ? '' : JSON.stringify(v)\n setInstallError((text(body.error) || humanOutput(text(body.stderr)) || 'error').trim().slice(-600))\n }\n })\n .catch(error => setInstallError(String(error)))\n .finally(() => setRemovingName(null))\n // hotNames/refreshNames are read above to tell a plugin this page loaded\n // from one installed inside it, so they belong in the closure.\n }, [refreshInstalled, hotNames, refreshNames])\n\n /** Live enable/disable of one installed plugin (#60). `reload` opts the\n * card-level theme flow into a page refresh so the visual result lands\n * immediately (mirrors the use-skin reload on activate). */\n const doToggle = useCallback((name: string, enabled: boolean, reload = false) => {\n setTogglingName(name)\n setInstallError(null)\n return fetch(api('/dsh-market/toggle'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ name, enabled }),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n if (Array.isArray(body.disabled)) setDisabledNames(body.disabled)\n if (Array.isArray(body.live)) setSkins(body.live)\n if (body.activation && typeof body.activation === 'object') {\n setActivations(prev => ({ ...prev, ...body.activation }))\n }\n // A toggle whose fiber did not follow the switch joins the\n // pending-restart banner (same path as installs/updates/removals).\n if (body.restart === true) setToggleRestart(n => n + 1)\n // A client-part plugin's UI is already in the page — refresh to\n // show the change (mirrors the install hot banner).\n // Back to the position the page was rendered with means there is\n // nothing left for a refresh to show, so the banner drops it\n // instead of counting the round trip as a pending change (#340).\n if (body.refresh === true) {\n const wasDisabled = loadedDisabled.current?.has(name) ?? false\n if (wasDisabled === !enabled) clearPendingRefresh(name)\n else setRefreshNames(names => names.includes(name) ? names : names.concat(name))\n }\n // Not on the reload path: the page is about to go away, and the\n // theme flow lands its own toast on the other side.\n if (!reload) setToggled({ name, enabled })\n refreshInstalled()\n if (reload) {\n // Land back in the Themes tab with the stock look on screen.\n // Drop a stale install/switch toast so it cannot resurrect.\n sessionStorage.removeItem('dshm-toast')\n sessionStorage.removeItem('dshm-toast-mode')\n sessionStorage.setItem('dshm-tab', 'themes')\n location.reload()\n }\n } else {\n const text = (v: unknown) => typeof v === 'string' ? v : v == null ? '' : JSON.stringify(v)\n // The server's bilingual reason (e.g. host cannot hot-mount —\n // restart required) beats the generic failure line.\n setInstallError(text(body.reason) || text(body.error) || t('toggleFail'))\n // The durable state (state.json + patch layer) was still written,\n // so a restart applies it even though the live drive failed.\n if (body.restart === true) setToggleRestart(n => n + 1)\n if (body.refresh === true) setRefreshNames(names => names.includes(name) ? names : names.concat(name))\n }\n })\n .catch(error => setInstallError(String(error)))\n .finally(() => setTogglingName(null))\n }, [clearPendingRefresh, refreshInstalled, t])\n\n /** Adopt the groups payload returned by POST /dsh-market/groups. */\n const setGroupPayload = useCallback((body: {\n groups?: Record\n groupOrder?: string[]\n disabled?: string[]\n }) => {\n if (body.groups && typeof body.groups === 'object') setGroups(body.groups)\n if (Array.isArray(body.groupOrder)) setGroupOrder(body.groupOrder)\n if (Array.isArray(body.disabled)) setDisabledNames(body.disabled)\n }, [])\n\n /** One POST /dsh-market/groups round trip (create/rename/delete/members/toggle). */\n const doGroupAction = useCallback((payload: Record): Promise => {\n setInstallError(null)\n return fetch(api('/dsh-market/groups'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n .then(res => res.json().then(body => ({ status: res.status, body })))\n .then(({ status, body }) => {\n if (status === 200 && body.ok) {\n setGroupPayload(body)\n // Batch toggles whose members did not follow the switch join the\n // pending-restart banner too.\n if (Array.isArray(body.restartMembers) && body.restartMembers.length > 0) {\n setToggleRestart(n => n + body.restartMembers.length)\n }\n if (Array.isArray(body.refreshMembers) && body.refreshMembers.length > 0) {\n setRefreshNames(names => [...new Set([...names, ...body.refreshMembers])])\n }\n refreshInstalled()\n return true\n }\n const text = (v: unknown) => typeof v === 'string' ? v : v == null ? '' : JSON.stringify(v)\n setInstallError(text(body.error) || t('toggleFail'))\n if (Array.isArray(body.restartMembers) && body.restartMembers.length > 0) {\n setToggleRestart(n => n + body.restartMembers.length)\n }\n if (Array.isArray(body.refreshMembers) && body.refreshMembers.length > 0) {\n setRefreshNames(names => [...new Set([...names, ...body.refreshMembers])])\n }\n return false\n })\n .catch(error => { setInstallError(String(error)); return false })\n }, [refreshInstalled, setGroupPayload, t])\n\n /** Approve the build scripts pnpm refused, then rerun what was blocked. */\n const approveAndRetry = useCallback((\n names: string[],\n resume: () => void,\n ) => {\n fetch(api('/dsh-market/approve-builds'), {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ packages: names }),\n })\n .then(res => res.json())\n .then((body) => {\n if (!body.ok) setInstallError(String(body.error || 'approve failed'))\n else resume()\n })\n .catch(error => setInstallError(String(error)))\n }, [])\n\n const doGroupToggle = useCallback((name: string, enabled: boolean) => {\n return doGroupAction({ action: 'toggle', name, enabled })\n }, [doGroupAction])\n\n const doCreateGroup = useCallback(() => {\n const name = newGroupName.trim()\n if (name === '') return\n void doGroupAction({ action: 'create', name }).then(ok => {\n if (ok) {\n setCreatingGroup(false)\n setNewGroupName('')\n }\n })\n }, [doGroupAction, newGroupName])\n\n const doRenameGroup = useCallback((name: string) => {\n const newName = renamingValue.trim()\n if (newName === '' || newName === name) {\n setRenamingGroup(null)\n return\n }\n void doGroupAction({ action: 'rename', name, newName }).then(ok => {\n if (ok) {\n setRenamingGroup(null)\n setRenamingValue('')\n }\n })\n }, [doGroupAction, renamingValue])\n\n const doDeleteGroup = useCallback((name: string) => {\n void doGroupAction({ action: 'delete', name }).then(ok => {\n if (ok) setDeletingGroup(null)\n })\n }, [doGroupAction])\n\n const doAssign = useCallback((name: string) => {\n const group = assignTarget\n if (group === '') return\n const members = groups[group] ?? []\n void doGroupAction({ action: 'set-members', name: group, members: [...members, name] }).then(ok => {\n if (ok) {\n setAssignFor(null)\n setAssignTarget('')\n }\n })\n }, [assignTarget, doGroupAction, groups])\n\n const doRemoveMember = useCallback((group: string, name: string) => {\n const members = (groups[group] ?? []).filter(member => member !== name)\n void doGroupAction({ action: 'set-members', name: group, members })\n }, [doGroupAction, groups])\n\n /** Add one installed plugin to a group (picker stays open for batch adds). */\n const doAddMember = useCallback((group: string, name: string) => {\n const members = groups[group] ?? []\n void doGroupAction({ action: 'set-members', name: group, members: [...members, name] })\n }, [doGroupAction, groups])\n\n // The market itself stays out of the batch: its update reloads this page\n // mid-run, which would strand the remaining items.\n const selfName = installed['dshmarket'] !== undefined ? 'dshmarket' : 'dsh-market'\n const updatableNames = Object.keys(installed).filter(\n name => name !== selfName && !updatedNames.includes(name) && updates[name] && updates[name].updateAvailable,\n )\n // Replacing a local source with its catalog source is deliberately not a\n // batch update: every such plugin has an existing, explicit confirmation\n // gate because the source switch cannot be rolled back.\n const batchUpdatableNames = updatableNames.filter(name => updates[name]?.restoreRequired !== true)\n const ignoredUpdateSet = useMemo(() => new Set(ignoredUpdateNames), [ignoredUpdateNames])\n const reminderUpdatableNames = updatableNames.filter(name => !ignoredUpdateSet.has(name))\n const reminderBatchUpdatableNames = batchUpdatableNames.filter(name => !ignoredUpdateSet.has(name))\n const selfUpdateAvailable = updates[selfName]?.updateAvailable === true && !updatedNames.includes(selfName)\n const reminderUpdateNames = [\n ...(selfUpdateAvailable ? [selfName] : []),\n ...updatableNames,\n ].filter(name => !ignoredUpdateSet.has(name))\n // The market manages itself from its own settings card (Settings → Plugins\n // → Plugin configuration), not as a row here — listing it in both places\n // read as two different controls for the same thing.\n const installedOtherCount = Object.keys(installed).filter(name => name !== selfName).length\n\n const ignoreUpdateNotices = useCallback((names: string[]) => {\n if (bootId === null || names.length === 0) return\n setIgnoredUpdateNames(current => {\n const next = [...new Set([...current, ...names])]\n try {\n sessionStorage.setItem(IGNORED_UPDATES_SESSION_KEY, JSON.stringify({ boot: bootId, names: next }))\n } catch { /* storage unavailable: keep the dismissal for this mount */ }\n return next\n })\n }, [bootId])\n\n const doUpdateAll = useCallback(() => {\n const names = reminderBatchUpdatableNames.slice()\n setUpdatingAll(true)\n const next = () => {\n const name = names.shift()\n if (name === undefined) {\n setUpdatingAll(false)\n return\n }\n doUpdate(name).then(next, next)\n }\n next()\n }, [reminderBatchUpdatableNames, doUpdate])\n\n const finishRestore = useCallback((body: { errors?: unknown; unportable?: unknown; bootErrors?: unknown }) => {\n const errors = Array.isArray(body.errors) ? body.errors as { name?: unknown; error?: unknown }[] : []\n // Machine-specific dependency paths (#205): a `link:/Users/…` spec from\n // the machine that wrote the backup names a directory that does not\n // exist here, so pnpm cannot satisfy it. Listed with the other restore\n // problems rather than in a banner of its own — from the operator's\n // side it is one question (\"what went wrong with my restore?\").\n const unportable = Array.isArray(body.unportable) ? body.unportable as { name?: unknown; spec?: unknown }[] : []\n // Partial failures surface inline in the Backup tab (previously a\n // window.alert); the restore itself still completes.\n // What the profile analysis says about the composition that just landed\n // (#205). The restore itself succeeded; these are the packages the\n // composition still needs, which otherwise surfaced only at the NEXT\n // boot, as a Loader error with nothing tying it back to the restore.\n const bootErrors = Array.isArray(body.bootErrors) ? body.bootErrors.map(String) : []\n setRestoreErrors([\n ...errors.map(item => `${String(item.name)}: ${String(item.error)}`),\n ...unportable.map(item => `${String(item.name)}: ${t('restoreUnportable')} (${String(item.spec)})`),\n ...bootErrors.map(line => `${t('restoreBootError')} ${line}`),\n ])\n setBackupRestored(true)\n setBackupMessage(t('restoreDone'))\n if (errors.length === 0) {\n setPendingBackup(null)\n setPendingDependencies({})\n }\n refreshInstalled(true)\n }, [refreshInstalled, t])\n\n const previewBackup = useCallback((backup: unknown) => {\n const dependencies = backupDependencies(backup)\n setPendingBackup(backup)\n setPendingDependencies(dependencies)\n setBackupMessage(t('restorePreviewDone'))\n setRestoreErrors([])\n setTab('installed')\n }, [t])\n\n /** Actually run the restore; the confirm dialog gates this (previously window.confirm). */\n const doRestore = useCallback(() => {\n if (pendingBackup === null) return Promise.resolve()\n setRestoreConfirmOpen(false)\n setBackupBusy(true)\n setBackupMessage(null)\n setRestoreErrors([])\n return fetch(api('/dsh-market/restore'), {\n method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ backup: pendingBackup }),\n }).then(async response => {\n const body = await response.json()\n if (!response.ok) throw new Error(String(body.error || 'restore failed'))\n finishRestore(body)\n }).catch(error => setBackupMessage(String(error))).finally(() => setBackupBusy(false))\n }, [finishRestore, pendingBackup])\n\n const runWebdav = useCallback((action: 'backup' | 'restore') => {\n if (webdavUrl.trim() === '') return\n setBackupBusy(true)\n setBackupMessage(null)\n setRestoreErrors([])\n fetch(api('/dsh-market/webdav'), {\n method: 'POST', headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ action, url: webdavUrl.trim(), username: webdavUser, password: webdavPassword }),\n }).then(async response => {\n const body = await response.json()\n if (!response.ok) throw new Error(String(body.error || 'WebDAV failed'))\n if (action === 'restore') {\n previewBackup(body.backup)\n }\n if (action === 'backup') {\n try { localStorage.setItem('dshm-webdav-last', String(Date.now())) } catch { /* storage unavailable */ }\n setBackupMessage(t('backupDone'))\n }\n }).catch(error => setBackupMessage(String(error))).finally(() => setBackupBusy(false))\n }, [previewBackup, t, webdavPassword, webdavUrl, webdavUser])\n\n /** Map the server's token-source string to a localized label. */\n const gistSourceLabel = (source: string): string => {\n if (source === 'token') return t('gistSrcToken')\n if (source === 'env') return t('gistSrcEnv')\n if (source === 'gh') return t('gistSrcGh')\n return source\n }\n\n /** Turn any failure (server error, network error, timeout) into a friendly message. */\n const gistErrorMessage = (error: unknown): string => {\n const err = error as { name?: unknown; code?: unknown }\n const name = typeof err?.name === 'string' ? err.name : ''\n const code = typeof err?.code === 'string' ? err.code : ''\n if (code === 'timeout' || name === 'TimeoutError' || name === 'AbortError') return t('gistErrTimeout')\n if (code === 'network') return t('gistErrNetwork')\n if (code === 'auth') return t('gistErrAuth')\n if (code === 'notfound') return t('gistErrNotFound')\n if (code === 'rate-limit') return t('gistErrRateLimit')\n if (code === 'invalid') return t('gistErrInvalid')\n // Network-level fetch failures surface as TypeError(\"Failed to fetch\").\n if (error instanceof TypeError) return t('gistErrNetwork')\n return String(error)\n }\n\n const runGist = useCallback((action: 'export' | 'import' | 'verify') => {\n setGistBusy(true)\n setGistMessage(null)\n setGistOk(false)\n setGistResult(null)\n setRestoreErrors([])\n setExportError(null)\n const body: Record = { action, token: gistToken.trim() }\n // Import always targets the field; export targets it only in update mode\n // (create mode deliberately ignores the field and makes a new Gist).\n if (action === 'import') body.gistId = gistId.trim()\n if (action === 'export' && gistMode === 'update') {\n if (gistId.trim() === '') {\n setGistBusy(false)\n setGistMessage(t('gistErrNoId'))\n setGistOk(false)\n return\n }\n body.gistId = gistId.trim()\n }\n if (action === 'export') {\n // All plugins selected → full backup (with config); partial → only\n // the checked plugins, config optional via the picker flag.\n const allNames = new Set([...Object.keys(installed), ...installedBundles])\n const allSelected = exportSelection.size === allNames.size && exportSelection.size > 0\n if (!allSelected) {\n body.includeDeps = [...exportSelection]\n if (exportIncludeConfig) body.includeConfig = true\n }\n }\n fetch(api('/dsh-market/gist'), {\n method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body),\n // Fallback ceiling only — the server answers structured errors (with a\n // code) within 25 s, so a wedged host cannot leave the user staring at\n // \"working…\" forever.\n signal: AbortSignal.timeout(30_000),\n }).then(async response => {\n let body: Record = {}\n try { body = await response.json() as Record } catch { /* non-JSON response */ }\n if (!response.ok) {\n const error = new Error(String(body.error || 'Gist failed'))\n if (typeof body.code === 'string') (error as { code?: string }).code = body.code\n throw error\n }\n if (action === 'export') {\n setGistResult(body as unknown as GistExportResult)\n // Backfill the id so the next export updates this Gist instead of\n // creating yet another one.\n const ref = body as unknown as GistExportResult\n if (typeof ref.gistId === 'string' && ref.gistId !== '') {\n setGistId(ref.gistId)\n // A fresh export flips the mode to update so the next export\n // PATCHes the same Gist instead of creating yet another one.\n setGistMode('update')\n try { localStorage.setItem('dshm-gist-id', ref.gistId) } catch { /* storage unavailable */ }\n }\n setGistMessage(t('gistExportDone'))\n setGistOk(true)\n setExportOpen(false)\n } else if (action === 'import') {\n previewBackup(body.backup)\n } else {\n // verify: tell the user which token source actually served the request.\n const source = typeof body.source === 'string' ? body.source : ''\n setGistMessage(t('gistVerifySource').replace('{0}', gistSourceLabel(source)))\n setGistOk(true)\n }\n }).catch(error => {\n const message = gistErrorMessage(error)\n setGistMessage(message)\n setGistOk(false)\n // Keep the picker open (selection preserved) and show the failure\n // inside it — never hidden behind the dialog.\n if (action === 'export') setExportError(message)\n }).finally(() => setGistBusy(false))\n }, [exportIncludeConfig, exportSelection, gistId, gistToken, installed, installedBundles, previewBackup, t])\n\n /** The picker list: dependency plugins + bundle-only plugins, deduplicated. */\n const exportOptions = useMemo(() => {\n const names = new Set([...Object.keys(installed), ...installedBundles])\n return [...names].sort()\n }, [installed, installedBundles])\n\n /** Classify an install spec for the export picker badge. */\n const specKind = (spec: string | undefined): 'npm' | 'git' | 'file' | 'bundle' => {\n if (spec === undefined) return 'bundle'\n if (/^file:/i.test(spec)) return 'file'\n if (/^(github:|git\\+|git:)/i.test(spec)) return 'git'\n return 'npm'\n }\n\n const openExportPicker = useCallback(() => {\n const names = new Set([...Object.keys(installed), ...installedBundles])\n setExportSelection(new Set(names))\n setExportIncludeConfig(false)\n setExportOpen(true)\n }, [installed, installedBundles])\n\n useEffect(() => {\n // Persist only the non-secret WebDAV settings; the password stays\n // server-side/in-memory (see savedWebdav). Storage itself may be\n // unavailable (e.g. the client test env), so never let it crash the UI.\n try {\n localStorage.setItem(WEBDAV_STORAGE_KEY, JSON.stringify({ url: webdavUrl, username: webdavUser, auto: autoBackup }))\n } catch { /* storage unavailable — config just won't survive reload */ }\n if (!autoBackup || webdavUrl.trim() === '') return\n let last = 0\n try {\n last = Number(localStorage.getItem('dshm-webdav-last')) || 0\n } catch { /* ignore */ }\n if (Date.now() - last >= 24 * 60 * 60 * 1000) runWebdav('backup')\n }, [autoBackup, runWebdav, webdavUrl, webdavUser])\n\n const sessionPendingRestart = doneUrls.length + updatedNames.length + removedCount + toggleRestart + (backupRestored ? 1 : 0)\n /**\n * Plugins the HOST reports as restart-pending, independent of what this\n * browser session happens to remember. Installing and then reloading the\n * page used to leave no restart affordance at all: the banner is built\n * from session state, while the Installed tab only says \"activates on\n * restart\" in passing — so the user was told a restart was needed and\n * given nothing to press. Dismissible, because a standing banner nobody\n * wants to act on right now is just noise (it returns next session, or\n * as soon as another change lands).\n */\n const hostPendingNames = Object.keys(activations).filter(name => activations[name]?.state === 'restart')\n const showHostPending = hostPendingNames.length > 0 && !restartNoticeDismissed && sessionPendingRestart === 0\n const pendingRestart = sessionPendingRestart > 0 ? sessionPendingRestart : (showHostPending ? hostPendingNames.length : 0)\n const displayedInstalled = pendingBackup === null ? installed : { ...pendingDependencies, ...installed }\n const missingRestoreCount = Object.keys(pendingDependencies).filter(name => !installedFiles.includes(name)).length\n // Self-update lives in the header button and the settings card, not this\n // tab's row list (the market itself is filtered out below) — so a pending\n // self-update alone must not light up a dot pointing at an empty-looking tab.\n const hasUpdates = reminderUpdatableNames.length > 0\n\n /** Live status line: structured phase, or the human-line fallback. */\n const phasePart = progressPhase != null\n ? phaseLabel(progressPhase, t)\n + (progressCurrent !== null ? ' · ' + progressCurrent : '')\n + (progressDone > 0 ? ' · ' + t('packagesDone').replace('{0}', String(progressDone)) : '')\n : progressLine || t('progressHint')\n const progressText = cancelling ? t('cancelling') + ' · ' + phasePart : phasePart\n\n /** Whether the catalog carries ANY theme-category entry at all — distinct\n * from `themePlugins.length === 0` above (which also fires the instant a\n * search/sort/time filter matches nothing) so the two empty states read\n * differently: \"there's nothing here yet\" vs \"nothing matches your filter\". */\n const anyThemePlugins = data === null ? [] : themePluginsOf(data.plugins)\n\n /** The catalog entry a deprecated plugin's `replacement` names, if any. */\n const replacementOf = (p: RegistryPlugin): RegistryPlugin | undefined =>\n p.deprecated === true && p.replacement !== undefined\n ? data?.plugins.find(r => r.name === p.replacement)\n : undefined\n\n const pluginCard = (p: RegistryPlugin) => {\n const desc = (p.description && (p.description[lang] || p.description.en)) || ''\n const done = doneUrls.includes(p.url) || hotUrls.includes(p.url)\n const already = isInstalled(p, catalogInstalled, repoIdentities, data?.plugins, repoHints)\n const busy = busyUrl === p.url\n const replacement = replacementOf(p)\n // The card reflects its own latest operation. Without this a rejected\n // install leaves the card looking untouched, and pressing Install again\n // is the obvious next move — which is how the same clash gets hit twice.\n const record = recordForUrl(records, p.url)\n const blocked = record !== null && (record.state === 'input' || record.state === 'failed')\n return (\n
\n
\n {/* The avatar belongs to the AUTHOR, not to the title. Beside the\n name it reads as one signature, which is what frees the title\n to be just the plugin — and lets two authors ship a plugin of\n the same name without either card needing a qualifier. */}\n
\n \n {pluginName(p.name)}\n \n {p.deprecated === true && {t('deprecatedBadge')}}\n \n
\n \n {p.owner}\n {typeof p.downloads === 'number' && (\n \n {'· ↓ ' + formatCount(p.downloads)}\n \n )}\n {typeof p.stars === 'number' && (\n \n {'· ★ ' + formatCount(p.stars)}\n \n )}\n
\n
\n {/* Top right, at its natural size: in the footer it needed a row of\n its own once the cards went two-up, which cost every card that\n height whether or not it had anything else to say. */}\n \n
\n {done\n ? {t('installedBadge')}\n : already\n ? {t('alreadyInstalled')}\n : busy\n ? \n : blocked\n ? (\n \n )\n : (\n setConfirming(p)}\n >{t('install')}\n )}\n
\n
\n \n \n {p.deprecated === true && (\n
\n
\n ⚠️ {t('deprecatedWarn')}\n {replacement !== undefined && (\n \n {t('replacementHint') + ' ' + replacement.name}\n \n )}\n
\n
\n )}\n
\n {pluginCategories(p).map(category => (\n \n {(data!.categories[category] && (data!.categories[category]![lang] || data!.categories[category]!.en)) || category}\n \n ))}\n {/* Published date and a source link used to live here too — both\n redundant now that the title itself opens the repo, and the\n date/tag pair alone was long enough in English to wrap onto its\n own line, splitting one card's footer into two visual rows. */}\n \n {/* No comment count here. Showing one would mean asking giscus about\n every card on the page just to render a number, and a row of\n zeroes reads as \"nobody uses these\" on a catalog where almost\n nothing has been commented on yet. */}\n \n
\n {busy && (\n
\n \n {progressText}\n {progressPct !== null && {progressPct}%}\n \n
\n \n
\n
\n )}\n
\n )\n }\n\n const installedNameOf = (p: RegistryPlugin) => matchInstalledName(p, installed, repoIdentities, data?.plugins, repoHints)\n\n // Plugins loaded at boot (bundle-layer skins) aren't in the shim list but\n // are just as live; the boot manifest is the page's own record of them.\n const bootEntries = (typeof window !== 'undefined' && window.__DSH_BOOT__ && Array.isArray(window.__DSH_BOOT__.entries))\n ? window.__DSH_BOOT__.entries\n : []\n\n // Theme-native card: visual preview first, then identity and lifecycle.\n // This deliberately does not reuse pluginCard; repository metadata is\n // useful context here, but it must not outrank the theme itself.\n const themePluginCard = (p: RegistryPlugin) => {\n const instName = installedNameOf(p)\n const desc = (p.description && (p.description[lang] || p.description.en)) || ''\n const replacement = replacementOf(p)\n const done = doneUrls.includes(p.url) || hotUrls.includes(p.url)\n const busy = busyUrl === p.url\n const record = recordForUrl(records, p.url)\n const blocked = record !== null && (record.state === 'input' || record.state === 'failed')\n // A theme switched off via the Installed-tab toggle (or a group switch)\n // stays in the boot manifest, so the disabled set must veto the badge.\n const mounted = instName !== null\n && (skins.includes(instName) || bootEntries.some(e => e.id === instName))\n && !effectiveDisabledSet.has(instName)\n\n return (\n
\n \n
\n
\n
\n \n {pluginName(p.name)}\n \n \n
\n \n {p.owner}\n {typeof p.downloads === 'number' && (\n \n {'· ↓ ' + formatCount(p.downloads)}\n \n )}\n {typeof p.stars === 'number' && (\n \n {'· ★ ' + formatCount(p.stars)}\n \n )}\n
\n
\n {p.deprecated === true && {t('deprecatedBadge')}}\n {mounted && {t('themeActive')}}\n {instName !== null && !mounted && (\n \n {effectiveDisabledSet.has(instName) ? t('disabledState') : t('alreadyInstalled')}\n \n )}\n
\n\n

{desc}

\n\n {p.deprecated === true && (\n
\n
\n ⚠️ {t('deprecatedWarn')}\n {replacement !== undefined && (\n \n {t('replacementHint') + ' ' + replacement.name}\n \n )}\n
\n
\n )}\n\n
\n {instName === null && (\n {done ? t('installedBadge') : t('notInstalled')}\n )}\n
\n {instName === null\n ? done\n ? {t('installedBadge')}\n : busy\n ? \n : blocked\n ? (\n \n )\n : (\n setConfirming(p)}\n >{t('install')}\n )\n : (\n <>\n {removingName === instName\n ? \n : }\n {mounted\n ? (\n doToggle(instName, false, true)}\n >{t('themeDeactivate')}\n )\n : }\n \n )}\n
\n
\n\n {busy && (\n
\n \n {progressText}\n {progressPct !== null && {progressPct}%}\n \n
\n \n
\n
\n )}\n
\n
\n )\n }\n\n const themeCard = (id: string, label: string, swatch: string[]) => {\n const active = themeSnap !== null && themeSnap.preference === id\n return (\n
\n
{swatch.map((c, i) => )}
\n
\n {label}\n \n {active\n ? {t('themeActive')}\n : (\n { try { props.theme.setTheme(id) } catch (error) { setInstallError(String(error)) } }}\n >{t('themeApply')}\n )}\n
\n
\n )\n }\n\n const categories = data === null ? [] : Object.keys(data.categories)\n\n useLayoutEffect(() => { setVisibleCats(null); setVisibleCatsOneRow(null) }, [lang, categories.length])\n useLayoutEffect(() => {\n if (catsOpen || visibleCats !== null) return\n const el = catsWrapRef.current\n if (el === null) return\n const chips = [...el.children].filter((c): c is HTMLElement => (c as HTMLElement).dataset?.chip === '1')\n if (chips.length === 0) return\n const first = chips[0]!\n const rowThreeTop = first.offsetTop + (first.offsetHeight + 6) * 2 - 3\n let fits = 0\n for (const chip of chips) { if (chip.offsetTop < rowThreeTop) fits += 1 }\n // Reserve the tail slot of row two for the chevron itself.\n setVisibleCats(fits >= chips.length ? fits : Math.max(1, fits - 1))\n // Same measuring pass, one row's worth instead of two — used only while\n // the sticky header is pinned during scroll (#188-adjacent request), to\n // shrink an OPEN multi-row category list down to its first row without\n // touching the user's actual open/closed choice.\n const rowTwoTop = first.offsetTop + first.offsetHeight + 6 - 3\n let fitsOneRow = 0\n for (const chip of chips) { if (chip.offsetTop < rowTwoTop) fitsOneRow += 1 }\n setVisibleCatsOneRow(fitsOneRow >= chips.length ? fitsOneRow : Math.max(1, fitsOneRow - 1))\n }, [catsOpen, visibleCats, data])\n\n useEffect(() => {\n if (catsSentinel === null || typeof IntersectionObserver === 'undefined') return\n const observer = new IntersectionObserver(\n ([entry]) => {\n const leftView = entry !== undefined && !entry.isIntersecting\n // Collapsing shrinks the sticky header, which shrinks the scrollable\n // content. When there is barely more content than viewport, that\n // makes scrollHeight drop below the current scroll position, the\n // browser CLAMPS scrollTop, the sentinel slides back into view, and\n // the row expands again — which grows the content, restores the\n // scroll, and starts over. Reported as the category bar flapping and\n // the list refusing to scroll (#266 by @hidge123), and reproduced\n // here: a filtered list went scrollTop 78 → 0 and snapped straight\n // back from one row to four.\n //\n // The guard is not a tuning constant, it is the feature's own\n // precondition: collapsing exists to reclaim vertical space while\n // scrolling a LONG list. If the scroller has less overflow than the\n // category row could give back, collapsing buys nothing and can only\n // start the loop, so it does not happen. Long lists — the case this\n // was built for — are unaffected.\n const root = bodyRef.current\n const wrap = catsWrapRef.current\n if (leftView && root !== null && wrap !== null) {\n const overflow = root.scrollHeight - root.clientHeight\n if (overflow <= wrap.offsetHeight) return\n }\n setCatsStuck(leftView)\n },\n { root: bodyRef.current, threshold: 0 },\n )\n observer.observe(catsSentinel)\n return () => observer.disconnect()\n }, [catsSentinel])\n /**\n * Becoming stuck auto-collapses an open row — a REAL `catsOpen` flip, not\n * a display-only override. An earlier version faked this by computing a\n * separate \"effectively open\" value for rendering while leaving `catsOpen`\n * itself true; the chevron's own click handler only ever toggled the real\n * `catsOpen`, so while stuck it flipped a value the render path had\n * already stopped consulting — clicking \"expand\" did nothing visible\n * (reported: \"吸顶滚动了之后,展开没反应了\"). Driving the same state the\n * chevron drives means the chevron always works, stuck or not.\n */\n const catsAutoCollapsedRef = useRef(false)\n useLayoutEffect(() => {\n if (catsStuck) {\n if (catsOpen) { setCatsOpen(false); catsAutoCollapsedRef.current = true }\n } else if (catsAutoCollapsedRef.current) {\n setCatsOpen(true)\n catsAutoCollapsedRef.current = false\n }\n }, [catsStuck])\n\n /**\n * A fresh install (hotUrls/hotNames) and a toggle/group action\n * (refreshNames) both end in the same place — \"reload the page\" — and\n * used to render as two near-identical banners stacked on top of each\n * other when both happened in one session (reported as \"为啥有三个状态横幅\n * 啊,太奇怪了\"). They're merged into one count and one banner; only the\n * restart banner (a full host restart, a different action entirely) stays\n * separate.\n */\n const pendingRefreshNames = useMemo(\n () => [...new Set([...hotNames, ...refreshNames])],\n [hotNames, refreshNames],\n )\n\n /** Installed plugins the market itself cannot group (#60). */\n const groupableNames = Object.keys(installed).filter(name => name !== 'dsh-market' && name !== 'dshmarket')\n /** Names already inside some group; everything else shows under \"ungrouped\". */\n const groupedNames = useMemo(() => new Set(Object.values(groups).flat()), [groups])\n const ungroupedNames = groupableNames.filter(name => !groupedNames.has(name))\n /** Installed package names the catalog classifies as themes (client-side\n * mirror of the server's classification; themes are exclusive per group). */\n const installedThemeNames = useMemo(() => {\n const names = new Set()\n if (data === null) return names\n for (const [name, spec] of Object.entries(installed)) {\n const entry = entryForDep(data.plugins, name, String(spec), repoIdentities[name], repoHints[name])\n if (entry !== undefined && pluginCategories(entry).includes('theme')) names.add(name)\n }\n return names\n }, [data, installed, repoIdentities, repoHints])\n\n return (\n \n
\n
\n \n

{t('nav')}

\n {/* A quiet pointer back to the project — most visitors reach the\n market through a client that embeds it, with no other way to\n find the repo it came from. */}\n dsh-market\n {version !== null && v{version}}\n {(() => {\n const self = installed['dshmarket'] !== undefined ? 'dshmarket' : 'dsh-market'\n const status = updates[self]\n return status && status.updateAvailable && !updatedNames.includes(self)\n && !ignoredUpdateSet.has(self)\n && (\n {\n setTab('installed')\n if (status.restoreRequired === true) askRestore(self)\n else doUpdate(self)\n }}\n >{updatingName === self ? t('updating') : status.restoreRequired === true ? t('restoreOnline') : t('marketUpdate')}\n )\n })()}\n {reminderBatchUpdatableNames.length >= 2 && (\n { setTab('installed'); doUpdateAll() }}\n >{updatingAll ? t('updating') : t('updateAll') + ' (' + reminderBatchUpdatableNames.length + ')'}\n )}\n {bootId !== null && reminderUpdateNames.length > 0 && (\n ignoreUpdateNotices(reminderUpdateNames)}\n >{t('ignoreAllUpdateNotices')}\n )}\n
\n
\n {t('subtitle')}\n {t('submitPlugin')}\n \n }\n disabled={exportState === 'busy'}\n onClick={doExportLog}\n >{exportState === 'busy' ? t('exportingLog') : t('exportLog')}\n
\n
\n \n {themeSnap !== null && }\n \n { if (tab !== 'backup' && tab !== 'diagnostics') setTab('backup') }}\n >{t('tabAdvanced')}\n \n {/* In the tab row, not above the grid: paginating, searching and\n switching tab all leave it — and any pending decision — in place. */}\n setRecords(list => clearSettled(list))}\n onCancel={() => doCancel()}\n onDismiss={record => setRecords(list => drop(list, record.id))}\n onRefresh={() => location.reload()}\n onResolveConflict={resolveConflict}\n onApproveBuilds={(record) => {\n const names = record.blockedBuilds ?? []\n if (names.length === 0) return\n setRecords(list => drop(list, record.id))\n const plugin = record.url === undefined ? undefined : data?.plugins.find(p => p.url === record.url)\n approveAndRetry(names, () => {\n if (plugin !== undefined) doInstall(plugin)\n else doUpdate(record.name, false, false)\n })\n }}\n />\n
\n {/* Backup & Restore and Diagnostics sit under Advanced rather than as\n their own top-level tabs — most users never need either, and having\n five peers up top buried the ones people actually reach for. */}\n {(tab === 'backup' || tab === 'diagnostics') && (\n
\n \n \n \n
\n )}\n {!envReady && (\n
\n \n {envFailed ? t('envFixFail') : t('envMissing')}\n {!envFailed && (\n \n )}\n
\n )}\n {backupMessage !== null &&
{backupMessage}
}\n {restoreErrors.length > 0 && (\n
\n \n \n
{t('restorePartial')}
\n {restoreErrors.map(error =>
{error}
)}\n
\n
\n )}\n {tab === 'installed' && pendingBackup !== null && (\n
\n \n {t('restoreMissing').replace('{0}', String(missingRestoreCount))}\n \n
\n )}\n {pendingRefreshNames.length > 0 && (\n
\n \n {pendingRefreshNames.length} {t('refreshBanner')}\n {\n if (hotNames.length > 0) sessionStorage.setItem('dshm-toast', JSON.stringify(hotNames))\n sessionStorage.setItem('dshm-tab', 'installed')\n location.reload()\n }}\n >{t('refresh')}\n
\n )}\n {pendingRestart > 0 && (\n
\n \n {pendingRestart} {t('restartBanner')}\n \n \n \n {restartEnabled && debuggerLatch === null && (\n {restarting ? t('restarting') : t('restartNow')}\n )}\n {/* Only the standing host-reported notice is dismissible: a\n banner for something you just did in this session should not\n be swipeable away mid-flow. */}\n {showHostPending && (\n {\n setRestartNoticeDismissed(true)\n try { sessionStorage.setItem('dshm-restart-dismissed', String(bootId ?? '')) } catch { /* storage unavailable */ }\n }}\n >{t('dismiss')}\n )}\n
\n )}\n {activationWarnings.length > 0 && (\n
\n \n \n {activationWarnings.map(({ name, info }) => (\n
\n {name} — {activationMeta(info.state, t).label}\n {info.reasons.length > 0 && ({info.reasons.join(' / ')})}\n
\n ))}\n
\n
\n )}\n {tab === 'installed' && }\n
\n {buildsSkipped !== null && (\n
\n \n {t('buildsSkipped')} {buildsSkipped.names.join(', ')}\n {\n const { plugin, updateName, names, restore } = buildsSkipped\n setBuildsSkipped(null)\n approveAndRetry(names, () => {\n if (plugin !== undefined) doInstall(plugin)\n else if (updateName !== undefined) doUpdate(updateName, false, restore === true)\n })\n }}\n >{t('approveBuilds')}\n
\n )}\n {compatibilityNotice !== null && (\n
\n \n {/* Two independent findings share one banner and one rollback,\n because they came from one operation. Each is named for what\n it actually is: a peer-version risk and a loader-name\n collision are not the same problem and must not read as one. */}\n {compatibilityNotice.risks.length > 0 && (\n <>{t(compatibilityNotice.rollbackId === undefined ? 'compatRiskBannerNoRollback' : 'compatRiskBanner')} {compatibilitySummary(compatibilityNotice.risks)}\n )}\n {compatibilityNotice.shadowedNames !== undefined && compatibilityNotice.shadowedNames.length > 0 && (\n <>\n {compatibilityNotice.risks.length > 0 && ' · '}\n {t('shadowNameBanner')} {shadowSummary(compatibilityNotice.shadowedNames)}\n \n )}\n {compatibilityNotice.brokenBundles !== undefined && compatibilityNotice.brokenBundles.length > 0 && (\n <>\n {(compatibilityNotice.risks.length > 0\n || (compatibilityNotice.shadowedNames?.length ?? 0) > 0) && ' · '}\n {t('brokenBundleBanner')}{' '}\n {compatibilityNotice.brokenBundles.map(entry => entry.name).join(', ')}\n \n )}\n \n \n {compatibilityNotice.rollbackId === undefined\n ? {compatibilityNotice.rollbackUnavailable ?? t('rollbackUnavailable')}\n : (\n \n )}\n
\n )}\n {installError !== null && (\n
\n {installError}\n
\n {staleName !== null && (\n \n )}\n {restoreName !== null && (\n \n )}\n {/* The banner text told users to export the log; now it IS the button (#84). */}\n }\n disabled={exportState === 'busy'}\n onClick={doExportLog}\n >\n {exportState === 'busy' ? t('exportingLog') : t('exportLog')}\n \n
\n
\n )}\n setShowTop(e.currentTarget.scrollTop > 400)}\n >\n {tab === 'backup'\n ? (\n
\n
\n

{t('backupLocal')}

\n

{t('backupHint')}

\n

{t('credsWarning')}

\n
\n }\n disabled={backupBusy}\n onClick={() => downloadFile(api('/dsh-market/backup'), 'dsh-profile-backup.json')}\n >{backupBusy ? t('backupWorking') : t('backupDownload')}\n }\n disabled={backupBusy}\n onClick={() => fileInputRef.current?.click()}\n >{backupBusy ? t('backupWorking') : t('backupImport')}\n {\n const file = event.currentTarget.files?.[0]\n event.currentTarget.value = ''\n if (file !== undefined) file.text().then(text => previewBackup(JSON.parse(text))).catch(error => setBackupMessage(String(error)))\n }}\n />\n
\n
\n
\n

{t('webdav')}

\n setPresetOpen(false)}\n onSelect={id => {\n const urls: Record = {\n jianguoyun: 'https://dav.jianguoyun.com/dav/dsh-profile-backup.json',\n koofr: 'https://app.koofr.net/dav/Koofr/dsh-profile-backup.json',\n nextcloud: 'https://nextcloud.example/remote.php/dav/files/USERNAME/dsh-profile-backup.json',\n }\n if (urls[id] !== undefined) setWebdavUrl(urls[id]!)\n }}\n align=\"start\"\n anchor={(\n }\n onClick={() => setPresetOpen(o => !o)}\n >{t('webdavPreset')}\n )}\n items={[\n { id: 'custom', label: t('webdavPreset') },\n { id: 'jianguoyun', label: '坚果云 / Nutstore' },\n { id: 'koofr', label: 'Koofr' },\n { id: 'nextcloud', label: 'Nextcloud' },\n ]}\n />\n } type=\"url\" value={webdavUrl} placeholder={t('webdavUrl')} onChange={e => setWebdavUrl(e.target.value)} />\n setWebdavUser(e.target.value)} />\n setWebdavPassword(e.target.value)} />\n
\n \n \n
\n \n

{t('webdavNote')}

\n

{t('credsWarning')}

\n
\n
\n

{t('gist')}

\n setGistToken(e.target.value)}\n />\n }\n value={gistId}\n placeholder={t('gistId')}\n onChange={e => setGistId(e.target.value)}\n />\n
\n \n \n
\n
\n \n \n \n
\n {gistResult !== null && (\n

\n {t('gistCreated')}{' '}\n {gistResult.gistUrl}\n

\n )}\n {gistMessage !== null && (\n
{gistMessage}
\n )}\n

{t('gistNote')}

\n
\n
\n )\n : tab === 'discover'\n ? loadError !== null\n ?
\n
{t('loadFail')}
\n
{loadError}
\n \n
\n : data === null\n ?
{t('loading')}
\n : (\n <>\n
\n
\n
\n } placeholder={t('searchPh')} value={q} onChange={e => setQ(e.target.value)} />\n
\n
\n
\n {/* The height cap belongs to the MEASURING pass only: that pass\n renders every chip so their offsets can be counted, and\n clipping hides the tall row from the user for the frame it\n exists. Applying it while OPEN clipped the very rows the\n user had just asked to see — with 20 categories, expanding\n revealed two rows out of six and read as \"nothing\n happened\". Collapsed after measuring needs no cap: the list\n is already sliced to what fits. */}\n
\n {(() => {\n // Collapsed, the selected category is pulled to the front so it never hides.\n // Whenever collapsed (default, or auto-collapsed by the sticky\n // header going stuck — see catsAutoCollapsedRef above), a stuck\n // header uses the one-row budget instead of the two-row one so an\n // already-open list that just got pinned shrinks further.\n const budget = catsStuck ? visibleCatsOneRow : visibleCats\n const ordered = orderedCategories(categories, cat, catsOpen, budget)\n const shown = catsOpen || budget === null ? ordered : ordered.slice(0, Math.max(0, budget - 1))\n return (\n <>\n setCat('all')}>{t('all') + ' (' + formatCount(data!.count) + ')'}\n {shown.map(id => (\n setCat(id)}\n >{(data.categories[id] && (data.categories[id]![lang] || data.categories[id]!.en)) || id}\n ))}\n : }\n aria-label={catsOpen ? t('catsLess') : t('catsMore')}\n onClick={() => {\n // An explicit click always wins — don't let the next\n // stuck/unstuck transition second-guess it.\n catsAutoCollapsedRef.current = false\n setCatsOpen(o => !o)\n }}\n />\n \n )\n })()}\n
\n \n
\n
\n
\n {plugins.length === 0\n ?
{t('empty')}
\n : (\n <>\n \n \n \n )}\n \n )\n : tab === 'themes' && themeSnap !== null\n ? (\n <>\n
\n } placeholder={t('searchPh')} value={qThemes} onChange={e => setQThemes(e.target.value)} />\n
\n \n \n }\n aria-label={themesFullscreen ? t('themeExitFullscreen') : t('themeFullscreen')}\n aria-pressed={themesFullscreen}\n onClick={() => setThemesFullscreen(value => !value)}\n />\n \n
\n
\n {/* Light/dark/system live in the official Appearance setting; this\n tab only shows what that setting can't: registered third-party\n palettes (none in the wild yet) and installable theme plugins. */}\n {(() => {\n const extra = themeSnap.themes.filter(def => def.id !== 'light' && def.id !== 'dark')\n return extra.length > 0 && (\n
\n {extra.map(def => themeCard(def.id, def.id, themeSwatch(def)))}\n
\n )\n })()}\n {data === null\n ?
{t('loading')}
\n : anyThemePlugins.length === 0\n ?
{t('themeEmpty')}
\n : themePlugins.length === 0\n ?
{t('empty')}
\n : (\n <>\n
\n {t('themeResultCount').replace('{0}', String(themePlugins.length))}\n
\n
\n {themePagePlugins.map(themePluginCard)}\n
\n \n \n )}\n \n )\n : tab === 'diagnostics'\n ? \n : (\n <>\n
\n \n \n
\n
\n } placeholder={t('searchPh')} value={qInstalled} onChange={e => setQInstalled(e.target.value)} />\n
\n {installedView === 'groups'\n ? (\n <>\n
\n {creatingGroup\n ? (\n <>\n setNewGroupName(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') doCreateGroup() }} autoFocus />\n \n \n \n )\n : }\n
\n {groupOrder.length === 0\n ?
{t('noGroups')}
\n : groupOrder.map(gid => {\n const members = groups[gid] ?? []\n const sw = groupSwitchState(members, effectiveDisabledSet)\n return (\n
\n
\n doGroupToggle(gid, sw !== 'on')}\n >\n \n \n {gid}\n {sw === 'mixed' && {t('groupMixed')}}\n \n
\n {renamingGroup === gid\n ? (\n <>\n setRenamingValue(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') doRenameGroup(gid) }} autoFocus />\n \n \n \n )\n : }\n {deletingGroup === gid\n ? \n : }\n setAddPanel(\n addPanel !== null && addPanel.group === gid && addPanel.kind === 'plugin'\n ? null\n : { group: gid, kind: 'plugin' },\n )}\n >{t('groupAdd')}\n installedThemeNames.has(member))}\n onClick={() => setAddPanel(\n addPanel !== null && addPanel.group === gid && addPanel.kind === 'theme'\n ? null\n : { group: gid, kind: 'theme' },\n )}\n >{t('groupAddTheme')}\n
\n
\n {addPanel !== null && addPanel.group === gid && (() => {\n const candidates = addPanel.kind === 'theme'\n ? [...installedThemeNames].filter(name => !members.includes(name))\n : groupableNames.filter(name => !members.includes(name) && !installedThemeNames.has(name))\n return (\n
\n {candidates.length === 0\n ?
{t('groupAddEmpty')}
\n : candidates.map(name => (\n
\n {name}\n {effectiveDisabledSet.has(name) && {t('disabledState')}}\n \n \n
\n ))}\n
\n )\n })()}\n
\n {members.length === 0 &&
{t('groupEmpty')}
}\n {members.map(member => (\n
\n {member}\n {effectiveDisabledSet.has(member) && {t('disabledState')}}\n \n doToggle(member, effectiveDisabledSet.has(member))}\n >\n \n \n \n
\n ))}\n
\n
\n )\n })}\n
{t('ungrouped')}
\n {ungroupedNames.length === 0\n ?
{t('installedEmpty')}
\n : ungroupedNames.map(name => {\n const entry = data === null ? undefined : entryForDep(data.plugins, name, String(installed[name]), repoIdentities[name], repoHints[name])\n const off = effectiveDisabledSet.has(name)\n return (\n
\n
\n
\n {name}\n {entry?.deprecated === true && {t('deprecatedBadge')}}\n
\n
\n {off\n ? {t('disabledState')}\n : {t('stateLive')}}\n
\n
\n \n {assignFor === name\n ? (\n
\n \n \n \n
\n )\n : }\n
\n )\n })}\n \n )\n : Object.keys(displayedInstalled).filter(name => name !== selfName).length === 0\n ?
{t('installedEmpty')}
\n : (\n {\n // The market manages itself from its own settings\n // card, not as a row in this list (#188-adjacent).\n if (name === selfName) return false\n const needle = qInstalled.trim().toLowerCase()\n if (needle === '') return true\n if (name.toLowerCase().includes(needle)) return true\n if (String(spec).toLowerCase().includes(needle)) return true\n const entry = data === null ? undefined : entryForDep(data.plugins, name, String(spec), repoIdentities[name], repoHints[name])\n if (entry !== undefined) {\n const desc = (entry.description && (entry.description[lang] || entry.description.en)) || ''\n if (desc.toLowerCase().includes(needle)) return true\n if ((entry.owner || '').toLowerCase().includes(needle)) return true\n }\n return false\n })}\n render={([name, spec]) => {\n const missing = pendingBackup !== null && !installedFiles.includes(name)\n const entry = data === null ? undefined : entryForDep(data.plugins, name, String(spec), repoIdentities[name], repoHints[name])\n const status = updates[name]\n const localDev = /^(?:link|file):/i.test(String(spec)) || status?.kind === 'linked'\n const act = activations[name]\n const meta = act !== undefined ? activationMeta(act.state, t) : null\n const version = status && status.version ? 'v' + status.version : ''\n const specText = String(spec)\n // A plain range beside the resolved version says the\n // same thing twice. Every other spec — github:, file:,\n // link:, a tag — is the only place the row says where\n // the plugin came from, so it stays.\n const specRedundant = version !== '' && /^[\\^~]?\\d/.test(specText)\n const ghSpec = /^github:([A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+?)(?:#|$)/.exec(specText)\n const repoUrl = entry !== undefined ? entry.url : ghSpec !== null ? 'https://github.com/' + ghSpec[1] : null\n const off = effectiveDisabledSet.has(name)\n // Switches only where they make sense: everything in\n // the disable list (to re-enable), plus live/restart\n // states. inert/broken rows keep their diagnosis\n // without a misleading toggle (#60).\n const toggleable = off || (act !== undefined && (act.state === 'live' || act.state === 'restart'))\n return (\n
\n
\n {/* Row-scoped, NOT `.nm` alone: `.nm` clips with\n overflow+ellipsis as one block, so with the name and\n the version as inline siblings the ellipsis landed at\n the end of the LINE and ate the version — a long\n scoped package name hid the one fact this row exists\n to state (#257 by @HualuozhE). Laying the row out as\n flex lets the name be the only thing that truncates.\n `.nm` is shared by six other places (discover titles,\n group rows, theme cards); changing it there would\n reflow all of them. */}\n
\n {/* The name is the link to the README. A separate button\n beside it pointed at the same page. */}\n \n {repoUrl !== null\n ? {name}\n : name}\n \n {entry?.deprecated === true && {t('deprecatedBadge')}}\n {version && {version}}\n
\n {specRedundant\n ? null\n : repoUrl !== null\n ? {specText}\n :
{specText}
}\n {/* The user's own note REPLACES the author's\n description (#347): a catalog blurb answers\n \"what is this\", written for strangers and\n often not in the reader's language, and\n cannot answer \"why did I install this\" —\n which is what someone with forty plugins\n is asking. The original stays one click\n away rather than being lost. */}\n {notingName === name\n ? (\n
\n setNoteDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') saveNote(name, noteDraft)\n if (e.key === 'Escape') setNotingName(null)\n }}\n />\n \n \n
\n )\n : (() => {\n const note = notes[name]\n const authored = (entry?.description && (entry.description[lang] || entry.description.en)) || ''\n const theirs = showTheirs.includes(name)\n const shown = note !== undefined && !theirs ? note : authored\n if (shown === '' && note === undefined) return null\n return (\n
\n {shown}\n {note !== undefined && authored !== '' && (\n setShowTheirs(list => theirs ? list.filter(n => n !== name) : list.concat(name))}\n >{theirs ? t('noteMine') : t('noteTheirs')}\n )}\n { setNoteDraft(note ?? ''); setNotingName(name) }}\n >{note === undefined ? t('noteAdd') : t('noteEdit')}\n
\n )\n })()}\n {/* Update-notes entry (#294). Only a row with an\n update pending renders it — a plugin that is\n up to date has nothing to preview — and it is\n one quiet line in the flow the row already\n reserves for conditional content, so rows\n without it are pixel-identical to before. */}\n {status !== undefined && status.updateAvailable && (\n
\n openNotes(name, status.current ?? null, status.latest ?? null, repoUrl)}\n >{`▸ ${t('notesLink')}`}\n {bootId !== null && (\n ignoredUpdateSet.has(name)\n ? {t('updateNoticeIgnored')}\n : (\n ignoreUpdateNotices([name])}\n >{t('ignoreUpdateNotice')}\n )\n )}\n
\n )}\n {!off && act !== undefined && meta !== null && (\n
\n {/* Only a state the switch does NOT already show earns a\n line here: \"installed but not active\" is news, \"live\"\n is what the switch is for. */}\n {meta.dot !== 'done' && (\n \n \n {meta.label}\n \n )}\n {act.state !== 'live' && act.reasons.length > 0 && (\n }\n title={t('actWhy')}\n open={whyOpen === name}\n expandable\n expandOnRowClick\n onToggle={() => setWhyOpen(whyOpen === name ? null : name)}\n className={css.actWhy}\n >\n
{act.reasons.join(' / ')}
\n \n )}\n
\n )}\n {entry !== undefined && entry.deprecated === true && (\n
\n
\n ⚠️ {t('deprecatedWarn')}\n {entry.replacement !== undefined && (\n {t('replacementHint') + ' ' + entry.replacement}\n )}\n
\n
\n )}\n {updatingName === name && (\n
\n \n {progressText}\n {progressPct !== null && {progressPct}%}\n \n
\n \n
\n
\n )}\n
\n {/* At half width the identity and the controls cannot\n share a line, so the row is two stacked bands. Left\n as one wrapping line, neighbouring cards broke at\n different points and stopped lining up.\n The market itself never reaches this row (filtered\n out above — it manages itself from its own settings\n card), so no self-toggle special case is needed. */}\n
\n {/* Dot + tag, the pairing the host's own plugin\n inventory uses for exactly this state. */}\n {!missing && (\n \n \n {off ? t('disabledState') : t('switchOnLabel')}\n \n )}\n {toggleable && (\n doToggle(name, off)}\n >\n \n \n )}\n {/* State and switch pack left, the operations\n pack right: with everything in one flow the\n switch's x depended on whether the update\n slot rendered a button or a tag. */}\n \n {entry !== undefined && entry.deprecated === true && entry.replacement !== undefined && (() => {\n const replacement = data?.plugins.find(r => r.name === entry.replacement)\n if (replacement === undefined) return null\n return (\n <>\n \n {!isInstalled(replacement, catalogInstalled, repoIdentities, data?.plugins, repoHints) && (\n \n )}\n \n )\n })()}\n {/* Status slot and Uninstall wrap as ONE unit. As\n sibling children of a wrapping flex row they broke\n apart independently, leaving the tag on one line and\n the button on the next (#242 by @Ztyss). Nested,\n the pair either fits or moves together, and the tag\n — already ellipsizing since #234 — is what gives up\n width first. */}\n \n {missing\n ? {t('notInstalled')}\n : updatedNames.includes(name)\n ? {act?.state === 'live' ? t('updatedLive') : t('updated')}\n : updatingName === name\n ? \n : status && status.updateAvailable\n ? (\n {\n if (status.restoreRequired === true) askRestore(name)\n else doUpdate(name)\n }}\n >{status.restoreRequired === true ? t('restoreOnline') : t('update')}\n )\n : localDev\n ? {t('linkedDev')}\n : {t('upToDate')}}\n {!missing && name !== 'dsh-market' && name !== 'dshmarket' && (\n removingName === name\n ? \n : (\n <>\n {localDev && status?.restoreRequired !== true && (\n askRestore(name)}\n >{t('restore')}\n )}\n setRemoveConfirm(name)}\n >{t('uninstall')}\n \n )\n )}\n \n
\n
\n )\n }}\n />\n )}\n \n )}\n
\n {showTop && (\n \n \n { const el = bodyRef.current; if (el) el.scrollTo({ top: 0, behavior: 'smooth' }) }}\n >\n \n \n )}\n {confirming !== null && (\n { setConfirming(null); setCmdOpen(false) }}\n title={t('confirmTitle') + ' ' + confirming.name + '?'}\n footer={(\n <>\n \n \n \n )}\n >\n {/* The detail dialog has to show at LEAST what the card already\n does — owner, downloads, stars, published date, category — a\n \"detail\" view that shows less than the summary it opened from\n is backwards. */}\n
\n \n {confirming.owner}\n {typeof confirming.downloads === 'number' && (\n \n {'· ↓ ' + formatCount(confirming.downloads)}\n \n )}\n {typeof confirming.stars === 'number' && (\n \n {'· ★ ' + formatCount(confirming.stars)}\n \n )}\n \n {pluginCategories(confirming).map(category => (\n \n {(data!.categories[category] && (data!.categories[category]![lang] || data!.categories[category]!.en)) || category}\n \n ))}\n
\n {confirming.added &&
{t('published') + ' ' + confirming.added}
}\n {/* The Modal primitive's own `description` prop is sized for a\n one-line subtitle under the title — a full plugin description\n rendered there read as an oversized heading, not body text\n (reported on a real host). Rendering it here, at the card's own\n size, also matches the card's own reading order: name, byline,\n description, then screenshots. */}\n \n \n }\n title={t('cmdDetails')}\n open={cmdOpen}\n expandable\n expandOnRowClick\n onToggle={() => setCmdOpen(o => !o)}\n >\n
{confirming.install}
\n \n {looksTerminal(confirming, lang) && (\n

\n \n {' ' + t('terminalWarn') + ' '}\n {t('readme')}\n

\n )}\n {confirming.deprecated === true && (() => {\n const replacement = replacementOf(confirming)\n return (\n
\n
\n ⚠️ {t('deprecatedWarn')}\n {replacement !== undefined && (\n \n {t('replacementHint') + ' ' + replacement.name}\n \n )}\n
\n
\n )\n })()}\n

{' ' + t('confirmWarn')}

\n \n )}\n {commentsFor !== null && (\n setCommentsFor(null)}\n t={t}\n />\n )}\n {lightbox !== null && (\n setLightbox(null)}\n t={t}\n />\n )}\n {removeConfirm !== null && (\n setRemoveConfirm(null)}\n title={t('uninstall') + ' ' + removeConfirm + '?'}\n description={t('uninstallConfirmDesc')}\n footer={(\n <>\n \n \n \n )}\n />\n )}\n {notesFor !== null && (\n setNotesFor(null)}\n /* The host's Modal renders its title node verbatim; the hand-written\n primitives.d.ts narrows the prop to string, so this cast documents\n intent rather than defeating a runtime check. */\n title={(notesFor.repoUrl !== null\n ? {notesFor.name}\n : notesFor.name) as unknown as string}\n footer={(\n \n )}\n >\n {/* The version line reads as versions when both ends are semver and\n as short shas when the plugin updates from git — a 40-char sha\n pair wraps the dialog into nonsense. */}\n {(notesFor.current !== null || notesFor.latest !== null) && (\n
\n {notesFor.current !== null && notesFor.current.length === 40\n ? notesFor.current.slice(0, 7)\n : notesFor.current}\n \n {notesFor.latest !== null && notesFor.latest.length === 40\n ? notesFor.latest.slice(0, 7)\n : notesFor.latest}\n
\n )}\n {notesState === 'loading' &&
{t('loading')}
}\n {notesState === 'fail' &&
{t('notesLoadFail')}
}\n {notesState === 'ready' && updateNotes !== null && (\n updateNotes.kind === 'release' ? (\n
\n
\n {t('notesRelease')}\n {updateNotes.release.tag !== null && {' ' + updateNotes.release.tag}}\n {updateNotes.release.publishedAt !== null && {' · ' + updateNotes.release.publishedAt.slice(0, 10)}}\n
\n {/* Author-written markdown, rendered through a deliberately\n tiny converter: everything lands as React text children\n (auto-escaped), so no HTML from the repo can ever become\n markup — headings, bullets, bold and inline code only. */}\n
{renderMarkdown(updateNotes.release.body || t('notesNone'))}
\n
\n )\n : updateNotes.kind === 'commits' ? (\n
\n
{t('notesCommits')}
\n {!updateNotes.commits.found &&
{t('notesCommitsRecent')}
}\n
    \n {updateNotes.commits.items.map(c => (\n
  • \n {c.date !== null ? c.date.slice(0, 10) : ''}\n {mdInline(c.message)}\n {notesFor.repoUrl !== null && (\n /* The commit itself on GitHub — the escape hatch when\n two lines of clamp hide exactly the detail wanted. */\n {c.sha.slice(0, 7)}\n )}\n
  • \n ))}\n
\n
\n )\n : updateNotes.kind === 'npm' ? (\n
\n
{t('notesNpm')}
\n
    \n {updateNotes.npmTimes.map(v => (\n
  • \n {v.date.slice(0, 10)}\n {v.version}\n
  • \n ))}\n
\n
\n )\n :
{t('notesNone')}
\n )}\n \n )}\n {restoreConfirmOpen && pendingBackup !== null && (\n setRestoreConfirmOpen(false)}\n title={t('restoreConfirm')}\n footer={(\n <>\n \n \n \n )}\n />\n )}\n {exportOpen && (\n setExportOpen(false)}\n title={t('gistExportSelect')}\n description={t('gistExportHint')}\n footer={(\n <>\n \n \n \n )}\n >\n {exportOptions.length === 0 &&

{t('gistNoPlugins')}

}\n {exportOptions.length > 0 && (\n <>\n
\n \n \n
\n
\n {exportOptions.map(name => (\n \n ))}\n
\n \n {exportIncludeConfig &&

{t('credsWarning')}

}\n {exportError !== null &&

{exportError}

}\n \n )}\n \n )}\n {/* Log-export feedback via the Toast primitive — body portal, so it\n never squeezes the subtitle row or the error banner. */}\n {exportState === 'done' && (\n } onDone={exportToastDone} />\n )}\n {exportState === 'fail' && (\n } onDone={exportToastDone} />\n )}\n {toggled !== null && (\n : }\n onDone={toggledDone}\n />\n )}\n \n )\n}\n","/**\n * The market's card on the plugin configuration page (dsh >= 0.1.0-rc.7).\n *\n * It manages the market ITSELF — version, update, remove — when the current\n * profile owns the dependency. A host-provided market keeps only its version\n * display and download-region controls. This page is where a user goes to deal\n * with a plugin,\n * and \"which version am I on / update it / get rid of it\" is the part of\n * that anybody can act on without knowing how DSH is put together.\n *\n * `allowRestart` deliberately does NOT appear here. It exists for hosts\n * where a supervisor (systemd, launchd, pm2, Docker `restart: always`, a\n * desktop wrapper) owns the process, and its audience is whoever wrote that\n * deployment — a person already editing config, not someone browsing\n * settings. As a switch it read as jargon to everyone else, which is worse\n * than absent: a control you cannot evaluate is a control you cannot safely\n * touch. It remains a config option.\n *\n * ## Why the chrome is hand-built (again), and why it now matches\n *\n * The host's own contract is that \"a plugin that ships a browser half owns\n * its own card\" — the plugins tab only lays out a flex column and dispatches\n * `settings.plugin.item`. So the container IS ours to draw, and a value\n * import from `dsh-client-ui-settings-plugins` would fail the client\n * bundle-purity gate anyway.\n *\n * What the first version got wrong was drawing something of its own\n * invention: a flat, always-expanded box next to rows that collapse and\n * carry a chevron. The fix is not a different component — `DisclosureRow` is\n * 24px chrome for compact flow rows, a different thing — but the same design\n * tokens, laid out the way the host lays out `PluginCard`. Classes below\n * mirror it one for one, so the market stops looking like it wandered in\n * from another product.\n */\n\nimport { createElement as h, Fragment, useCallback, useEffect, useRef, useState } from 'react'\nimport type { ReactElement } from 'react'\nimport { Button, IconChevronDownOutline14, IconLoadingOutline16 } from '@deepseek-ai/dsh-client-ui-primitives'\nimport css from './Market.module.css'\nimport { api, setGithubProxy } from './market-data.ts'\nimport type { Translate } from './market-data.ts'\n\n/** Keys the market leaves in the browser; cleared when the user purges. */\nconst BROWSER_KEYS = ['dshm-webdav', 'dshm-gist-id'] as const\n\nexport interface SettingsCardProps {\n t: Translate\n /**\n * Retire the market's own entry in the left settings menu.\n *\n * Called once the package is gone. Leaving \"插件市场\" in the menu after\n * the user removed it is the card asserting something the profile no\n * longer agrees with — and the section behind it can no longer talk to a\n * server that has disposed its routes.\n */\n onRemoved?: () => void\n}\n\ntype Channel = 'stable' | 'beta' | 'dev'\ntype Region = 'global' | 'china'\n\n/** What api('/dsh-market/status') tells this card. */\ninterface SelfStatus {\n version: string | null\n restart: boolean\n channel: Channel\n /** The channels the SERVER offers; the card never invents its own list. */\n channels: Channel[]\n /** Which mirrors every outbound request uses. */\n region: Region\n /** The regions the SERVER offers, on the same principle as `channels`. */\n regions: Region[]\n /** The region came from the network check, not from the user. */\n regionAuto: boolean\n /** Whether the profile package manager owns this market installation. */\n selfManaged: boolean\n}\n\n/** The subset of api('/dsh-market/status') this card reads. */\ninterface StatusBody {\n version?: string\n restart?: boolean\n channel?: string\n channels?: string[]\n region?: string\n regions?: string[]\n regionAuto?: boolean\n githubProxy?: string | null\n selfManaged?: boolean\n}\n\n/** What api('/dsh-market/updates') says about the market's own row. */\ninterface SelfUpdate {\n updateAvailable: boolean\n latest: string | null\n /** The channel points at this version, and it is not newer: a switch. */\n channelSwitch: string | null\n /** The current build came from a local package and must switch to the online release. */\n restoreRequired: boolean\n}\n\ntype Phase = 'idle' | 'confirming' | 'working' | 'removed' | 'updated' | 'failed'\n\n/** The market's own row as api('/dsh-market/updates') sends it. */\ninterface RawUpdate { updateAvailable?: boolean; latest?: string; channelSwitch?: string; restoreRequired?: boolean }\n\nconst CHANNELS: Channel[] = ['stable', 'beta', 'dev']\nconst asChannel = (value: unknown): Channel | null =>\n CHANNELS.includes(value as Channel) ? (value as Channel) : null\n\nconst CHANNEL_LABEL: Record = {\n stable: 'setChannelStable', beta: 'setChannelBeta', dev: 'setChannelDev',\n}\nconst CHANNEL_HINT: Record = {\n stable: 'setChannelStableHint', beta: 'setChannelBetaHint', dev: 'setChannelDevHint',\n}\n\nconst REGIONS: Region[] = ['global', 'china']\nconst asRegion = (value: unknown): Region | null =>\n REGIONS.includes(value as Region) ? (value as Region) : null\n\nconst REGION_LABEL: Record = {\n global: 'setRegionGlobal', china: 'setRegionChina',\n}\nconst REGION_HINT: Record = {\n global: 'setRegionGlobalHint', china: 'setRegionChinaHint',\n}\n\n/**\n * Read the server's answer, taking the list of channels FROM it.\n *\n * The card does not decide which channels exist: the server is what accepts\n * or refuses a selection, so a card drawing its own list could only ever\n * disagree with it.\n */\nfunction readStatus(body: StatusBody): SelfStatus {\n const offered = (body.channels ?? []).map(asChannel).filter((c): c is Channel => c !== null)\n const regions = (body.regions ?? []).map(asRegion).filter((r): r is Region => r !== null)\n return {\n version: body.version ?? null,\n restart: body.restart === true,\n channel: asChannel(body.channel) ?? 'stable',\n // A host too old to send the list still gets the two that predate it.\n channels: offered.length > 0 ? offered : ['stable', 'beta'],\n region: asRegion(body.region) ?? 'global',\n // A host with no region at all is a host from before this existed. It\n // downloads from the official sources, which is what `global` means, so\n // the row tells the truth about that host rather than hiding.\n regions: regions.length > 0 ? regions : REGIONS,\n regionAuto: body.regionAuto === true,\n selfManaged: body.selfManaged !== false,\n }\n}\n\nfunction readUpdate(own: RawUpdate): SelfUpdate {\n return {\n updateAvailable: own.updateAvailable === true,\n latest: own.latest ?? null,\n channelSwitch: own.channelSwitch ?? null,\n restoreRequired: own.restoreRequired === true,\n }\n}\n\n/**\n * Clear the market's browser-side leftovers.\n *\n * These are the only two things the market keeps in the browser, and the\n * server cannot reach either. Neither holds a credential — the WebDAV\n * password is never persisted and a Gist token is read from the environment,\n * never from disk — so this is tidiness, not a security step, and the copy\n * must not imply otherwise.\n */\nexport function clearBrowserState(storage: Pick): void {\n for (const key of BROWSER_KEYS) {\n try { storage.removeItem(key) } catch { /* storage unavailable */ }\n }\n}\n\nexport function SettingsCard({ t, onRemoved }: SettingsCardProps): ReactElement | null {\n const [open, setOpen] = useState(false)\n const [status, setStatus] = useState(null)\n const [update, setUpdate] = useState(null)\n const [phase, setPhase] = useState('idle')\n const [restoreConfirming, setRestoreConfirming] = useState(false)\n const [purge, setPurge] = useState(false)\n const [error, setError] = useState(null)\n /**\n * The last self-update was refused by pnpm's fresh-release safety wait\n * (#39). Only the market's own card can update the market, so without a\n * retry here there is NO way to take a just-published version — the\n * discover list's retry button covers every plugin except this one (#255).\n */\n const [stale, setStale] = useState(false)\n\n // Only once the row is opened: the plugin configuration page renders every\n // card at once, and an update check costs a registry round trip.\n //\n // `open` is the ONLY dependency, and re-entry is guarded by a ref rather\n // than by `status`. Keying it on state the effect itself sets meant the\n // first `setStatus` re-ran the effect, whose cleanup dropped the still\n // in-flight update check — so the update row never appeared. Measured by\n // the spec, not reasoned about.\n const probed = useRef(false)\n useEffect(() => {\n if (!open || probed.current) return\n probed.current = true\n let live = true\n void (async () => {\n try {\n const response = await fetch(api('/dsh-market/status'), { cache: 'no-store' })\n const body = (await response.json()) as StatusBody\n if (live) setStatus(readStatus(body))\n setGithubProxy(typeof body.githubProxy === 'string' ? body.githubProxy : null)\n } catch {\n if (live) {\n setStatus({\n version: null, restart: false, channel: 'stable', channels: ['stable', 'beta'],\n region: 'global', regions: REGIONS, regionAuto: false, selfManaged: true,\n })\n }\n }\n try {\n const response = await fetch(api('/dsh-market/updates'), { cache: 'no-store' })\n const body = (await response.json()) as { updates?: Record }\n const own = body.updates?.['dshmarket'] ?? body.updates?.['dsh-market']\n if (live && own !== undefined) setUpdate(readUpdate(own))\n } catch { /* an update check that fails leaves the row without an offer */ }\n })()\n return () => { live = false }\n }, [open])\n\n const post = useCallback(async (path: string, payload: unknown): Promise<{ ok?: boolean; error?: string }> => {\n const response = await fetch(path, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n return (await response.json()) as { ok?: boolean; error?: string }\n }, [])\n\n const onUpdate = useCallback((force = false) => {\n setRestoreConfirming(false)\n setPhase('working')\n setError(null)\n setStale(false)\n void (async () => {\n try {\n const body = await post(api('/dsh-market/update'), {\n name: 'dshmarket',\n ...(update?.restoreRequired === true ? { restore: true } : {}),\n ...(force ? { force: true } : {}),\n }) as {\n ok?: boolean; error?: string; stale?: boolean\n }\n if (body.ok === true) setPhase('updated')\n else {\n setStale(body.stale === true)\n setError(body.error ?? t('setSelfFailed'))\n setPhase('failed')\n }\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n setPhase('failed')\n }\n })()\n }, [post, t, update?.restoreRequired])\n\n const onRemove = useCallback(() => {\n setPhase('working')\n setError(null)\n void (async () => {\n try {\n const body = await post(api('/dsh-market/self-uninstall'), { confirm: true, purge })\n if (body.ok === true) {\n if (purge) clearBrowserState(localStorage)\n setPhase('removed')\n onRemoved?.()\n } else { setError(body.error ?? t('setSelfFailed')); setPhase('failed') }\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n setPhase('failed')\n }\n })()\n }, [onRemoved, post, purge, t])\n\n const busy = phase === 'working'\n const version = status?.version ?? null\n // A prerelease says so in its own version string; nothing else has to be\n // consulted to know the running build is one.\n const prerelease = version !== null && version.includes('-')\n\n /** Re-ask what this channel offers; the previous answer was for another one. */\n const refreshUpdate = useCallback(async (): Promise => {\n const response = await fetch(api('/dsh-market/updates') + '?force=1', { cache: 'no-store' })\n const body = (await response.json()) as { updates?: Record }\n const own = body.updates?.['dshmarket'] ?? body.updates?.['dsh-market']\n setUpdate(own === undefined ? null : readUpdate(own))\n }, [])\n\n /**\n * Select a channel — and show the one the SERVER accepted.\n *\n * This used to move the control first and ignore the answer, which was\n * harmless while every channel was permitted. It stopped being harmless\n * the moment one of them can be refused: a 403 would have left \"dev\"\n * highlighted on a profile that is not on it.\n */\n const onChannel = useCallback((next: Channel) => {\n setError(null)\n void (async () => {\n try {\n const body = await post(api('/dsh-market/channel'), { channel: next }) as { ok?: boolean; error?: string; channel?: string }\n if (body.ok !== true) { setError(body.error ?? t('setSelfFailed')); return }\n setStatus(current => (current === null ? current : { ...current, channel: asChannel(body.channel) ?? next }))\n await refreshUpdate()\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n }\n })()\n }, [post, refreshUpdate, t])\n\n /**\n * Select a download region — and show the one the SERVER accepted, on the\n * same reasoning as the channel above.\n *\n * A hand-made choice retires the one-time notice: the market no longer has\n * anything to explain once the user has answered for themselves.\n */\n const onRegion = useCallback((next: Region) => {\n setError(null)\n void (async () => {\n try {\n const body = await post(api('/dsh-market/region'), { region: next }) as { ok?: boolean; error?: string; region?: string }\n if (body.ok !== true) { setError(body.error ?? t('setSelfFailed')); return }\n const accepted = asRegion(body.region) ?? next\n setStatus(current => (current === null ? current : { ...current, region: accepted, regionAuto: false }))\n // Re-read the resolved proxy rather than deriving it here. The card\n // knows which regions exist because the server told it; it must not\n // start knowing what each one RESOLVES to, or the routing table would\n // have a second copy that can disagree with the first.\n try {\n const fresh = await fetch(api('/dsh-market/status'), { cache: 'no-store' })\n const status = (await fresh.json()) as StatusBody\n setGithubProxy(typeof status.githubProxy === 'string' ? status.githubProxy : null)\n } catch { /* images keep the previous route until the next load */ }\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n }\n })()\n }, [post, t])\n\n /** One label + hint block with an optional action, the host's row shape. */\n const row = (label: string, hint: string, action: ReactElement | null): ReactElement =>\n h('div', { className: css.setRow },\n h('div', { className: css.setLabelBox },\n h('div', { className: css.setLabel }, label),\n h('div', { className: css.setHint }, hint),\n ),\n action,\n )\n\n // The end state REPLACES the controls rather than sitting beside them: the\n // package is gone from disk, so an update button next to \"removed\" would\n // offer something that cannot happen.\n const body = phase === 'removed'\n ? row(t('setSelfRemoved'), t('setSelfRemovedHint'), null)\n : h(Fragment, null,\n status?.selfManaged === true ? row(\n // An older offer is not an update, and calling it one would have\n // the user click \"更新\" to go backwards. It IS what picking an\n // earlier channel asked for, so it is offered — under its own name.\n update?.updateAvailable === true && update.latest !== null\n ? `${t('setSelfUpdateReady')} ${update.latest}`\n : update?.channelSwitch != null\n ? `${t('setChannelSwitch')} ${update.channelSwitch}`\n : t('setSelfUpToDate'),\n phase === 'updated'\n ? t('setSelfUpdatedHint')\n : update?.channelSwitch != null\n ? t('setChannelSwitchHint')\n // Explains what the Update button does — only worth saying\n // when that button is actually on screen. Already up to date,\n // it read as an instruction for an action that wasn't there.\n : update?.updateAvailable === true ? t('setSelfUpdateHint') : t('setSelfUpToDateHint'),\n phase === 'updated'\n ? null\n : update?.updateAvailable === true\n ? h(Button, {\n variant: 'primary',\n size: 'sm',\n disabled: busy,\n onClick: () => {\n if (update.restoreRequired) setRestoreConfirming(true)\n else onUpdate()\n },\n }, update.restoreRequired ? t('restoreOnline') : t('setSelfUpdate'))\n : update?.channelSwitch != null\n ? h(Button, { variant: 'outline', size: 'sm', disabled: busy, onClick: () => onUpdate() }, t('setChannelSwitch'))\n : null,\n ) : null,\n status?.selfManaged === true && restoreConfirming\n ? h('div', { className: css.setConfirm },\n h('div', { className: css.setHint }, t('restoreHint')),\n h('div', { className: css.setActions },\n h(Button, {\n variant: 'ghost', size: 'sm', onClick: () => { setRestoreConfirming(false) },\n }, t('setSelfCancel')),\n h(Button, {\n variant: 'primary', size: 'sm', onClick: () => onUpdate(),\n }, t('restoreContinue')),\n ),\n )\n : null,\n status?.selfManaged === true ? row(t('setChannel'), t(CHANNEL_HINT[status.channel]),\n h('div', { className: css.setSeg },\n // Drawn from what the SERVER says is available, so the control\n // can never offer a channel the route would refuse.\n (status?.channels ?? ['stable', 'beta']).map(id => h('button', {\n key: id,\n type: 'button',\n className: status?.channel === id ? `${css.setSegBtn} ${css.setSegOn}` : css.setSegBtn,\n disabled: busy || status === null,\n // The dev option says what it is on hover rather than being\n // hidden behind a switch. A label a user can read beats a gate\n // plus the machinery that maintains it, and the row's own hint\n // repeats it in full once the channel is selected.\n title: id === 'dev' ? t('setChannelDevHint') : undefined,\n onClick: () => { onChannel(id) },\n }, t(CHANNEL_LABEL[id]))),\n )) : null,\n // Above the channel row and below the update row: this is about\n // getting plugins at all, which is the market's whole job, while the\n // channel is about which build of the market itself arrives.\n row(\n t('setRegion'),\n // The hint carries the explanation of an automatic choice when\n // there is one to make, and the plain description of the selected\n // region otherwise. One line either way — a notice that pushes the\n // description off screen would trade a permanent answer for a\n // temporary one.\n status?.regionAuto === true\n ? `${t(REGION_HINT[status.region])} ${t('setRegionAuto')}`\n : t(REGION_HINT[status?.region ?? 'global']),\n h('div', { className: css.setSeg },\n (status?.regions ?? REGIONS).map(id => h('button', {\n key: id,\n type: 'button',\n className: status?.region === id ? `${css.setSegBtn} ${css.setSegOn}` : css.setSegBtn,\n disabled: busy || status === null,\n onClick: () => { onRegion(id) },\n }, t(REGION_LABEL[id]))),\n )),\n status?.selfManaged === true ? row(t('setSelfRemove'), t('setSelfRemoveHint'),\n phase === 'confirming' || busy\n ? null\n : h(Button, {\n variant: 'outline',\n size: 'sm',\n className: css.setDanger,\n disabled: busy,\n onClick: () => { setPhase('confirming') },\n }, t('setSelfRemove'))) : null,\n status?.selfManaged === true && (phase === 'confirming' || busy)\n ? h('div', { className: css.setConfirm },\n h('div', { className: css.setHint }, t('setSelfConfirm')),\n h('label', { className: css.setCheck },\n h('input', { type: 'checkbox', checked: purge, onChange: () => { setPurge(!purge) } }),\n h('span', null, t('setSelfPurge')),\n ),\n // States the consequence of the CURRENT choice, both ways. The\n // default keeps things, and what \"keeping\" costs — plugins the\n // market switched off stay off, with the UI that could switch\n // them back on about to disappear — is the part a user cannot\n // work out on their own.\n h('div', { className: css.setHint }, purge ? t('setSelfPurgeOn') : t('setSelfPurgeOff')),\n h('div', { className: css.setActions },\n busy\n ? null\n : h(Button, {\n variant: 'ghost',\n size: 'sm',\n onClick: () => { setPhase('idle'); setPurge(false) },\n }, t('setSelfCancel')),\n h(Button, {\n variant: 'primary',\n size: 'sm',\n className: css.setDanger,\n disabled: busy,\n icon: busy ? h('span', { className: css.spin }, h(IconLoadingOutline16, { size: 16 })) : undefined,\n onClick: onRemove,\n }, busy ? t('setSelfWorking') : t('setSelfRemoveConfirm')),\n ),\n )\n : null,\n error !== null ? h('div', { className: css.err }, error) : null,\n // The one place the market can be forced past pnpm's fresh-release\n // wait. Shown only after that specific refusal, never as a default:\n // the wait exists because a version published minutes ago can still\n // be unpublished (#39).\n stale\n ? h('div', { className: css.setActions },\n h(Button, { variant: 'primary', size: 'sm', onClick: () => onUpdate(true) }, t('updateNow')))\n : null,\n )\n\n return h('div', { className: open ? `${css.setCard} ${css.setCardOpen}` : css.setCard },\n h('button', {\n type: 'button',\n className: css.setHeader,\n 'aria-expanded': open,\n onClick: () => { setOpen(!open) },\n },\n h('div', { className: css.setHeadText },\n h('div', { className: css.setName },\n t('nav'),\n version !== null ? h('span', { className: css.version }, ` v${version}`) : null,\n prerelease ? h('span', { className: css.setBetaTag }, t('setChannelBeta')) : null,\n ),\n h('div', { className: css.setDesc }, t('setCardDesc')),\n ),\n h('span', { className: open ? `${css.setChevron} ${css.setChevronOpen}` : css.setChevron },\n h(IconChevronDownOutline14, { size: 14 }),\n ),\n ),\n open ? h('div', { className: css.setBody }, body) : null,\n )\n}\n","/**\n * dsh-market client: registers a \"Market\" settings section rendering the\n * plugin market UI, plus the post-install toast in the shell overlay layer.\n * Built by tsdown into the __ModuleLoader__ factory bundle at\n * client/client.js; the only externals are the loader module table's react\n * entries.\n */\nimport { createElement as h } from 'react'\nimport * as primitives from '@deepseek-ai/dsh-client-ui-primitives'\nimport { en, zh } from './locales.ts'\nimport { InstallToast } from './InstallToast.tsx'\nimport { MarketSection } from './MarketSection.tsx'\nimport { SettingsCard } from './SettingsCard.tsx'\nimport type { ThemeSnapshot, Translate } from './market-data.ts'\n\nconst NS = 'dsh-market'\n\n/**\n * Primitives this bundle relies on that did not exist before rc.6. The\n * primitives module is host-injected (external at build time), so on an\n * older host the module resolves but these named exports are undefined —\n * rendering would throw and blank the whole settings dialog. Returning the\n * gaps lets apply() skip registration for a clean downgrade instead.\n */\nexport const REQUIRED_PRIMITIVES = ['Menu', 'DisclosureRow', 'Tooltip', 'Toast'] as const\n\nexport function missingPrimitives(mod: Record, required: readonly string[] = REQUIRED_PRIMITIVES): string[] {\n return required.filter(name => mod[name] === undefined)\n}\n\n/**\n * The host surface the settings card needs, present only on rc.7+.\n *\n * The card no longer reads or writes settings — it manages the market's own\n * package — but `settingsScope` stays as the INJECTION KEY, because its\n * presence is what distinguishes a host that has the plugin configuration\n * page from one that does not. The market's namespace (registered in\n * settings.ts) is likewise still required: the page dispatches a card keyed\n * by a namespace it serves, so dropping it would take the card with it.\n */\ninterface SettingsScopeHost {\n slots: {\n inject(name: string, register: () => unknown): void\n register(options: Record, render: () => unknown): unknown\n }\n}\n\n/** The subset of the theme service this plugin touches. */\ninterface ThemeService {\n getTheme(): ThemeSnapshot | null\n setTheme(id: string): void\n}\n\n/** The subset of the locale service this plugin touches. */\ninterface LocaleService {\n register(namespace: string, dicts: { zh: Record; en: Record }): unknown\n bind(namespace: string): Translate\n subscribe(callback: () => void): () => void\n getSnapshot(): { active: string }\n}\n\n/** The subset of the slots service this plugin touches. */\ninterface SlotsService {\n inject(slot: string, register: () => unknown): void\n register(meta: Record, component: () => unknown): unknown\n}\n\n/** The client cordis context shape this plugin relies on (structural: the\n * host provides the real Context; typing the touched surface keeps this\n * external package free of monorepo-internal type dependencies). */\ninterface MarketClientContext {\n effect(callback: () => unknown, label?: string): void\n on(event: string, callback: () => void): () => void\n locale: LocaleService\n slots: SlotsService\n theme: ThemeService\n}\n\nexport const name = 'dsh-market'\n// 'theme' is safe to require: ui-layout (mandatory in every web composition)\n// already hard-depends on it. This cordis's object-form inject means\n// intercept config, NOT {required,optional} — do not use it here.\nexport const inject = ['slots', 'locale', 'theme']\nexport function apply(ctx: MarketClientContext): void {\n // Older hosts resolve the primitives module but lack the rc.6 exports the\n // market renders with. Skip registration (market simply absent from the\n // settings list) rather than throwing mid-render and blanking the dialog.\n const gaps = missingPrimitives(primitives as unknown as Record)\n if (gaps.length > 0) {\n console.warn('[dsh-market] host ui-primitives missing ' + gaps.join(', ') + ' — market section disabled (dsh web >= 0.1.0-rc.6 required)')\n return\n }\n\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-market: dictionaries')\n const t = ctx.locale.bind(NS)\n\n // Kept so the removal flow can retire the market's own nav entry the\n // moment the package is gone: leaving \"插件市场\" in the left menu after\n // the user removed it is the card claiming something the profile no\n // longer agrees with. `register` hands back its own disposer; calling it\n // twice (here and again when the context unwinds) is harmless, but the\n // reference is dropped after use so the intent stays readable.\n let retireSection: (() => void) | null = null\n\n ctx.slots.inject('settings.section', () => {\n const off = ctx.slots.register({\n name: 'settings.section',\n id: 'market',\n order: 40,\n label: () => t('nav'),\n locale: NS,\n inject: () => ({ t }),\n }, (ownerProps: { preferredSubsectionId?: string } = {}) => h(MarketSection, {\n t,\n locale: ctx.locale,\n theme: ctx.theme,\n themeStore: {\n subscribe: (cb: () => void) => ctx.on('theme/change', cb),\n getSnapshot: () => ctx.theme.getTheme(),\n },\n preferredSubsectionId: ownerProps.preferredSubsectionId,\n }))\n if (typeof off === 'function') retireSection = off as () => void\n return off\n })\n\n // The settings card (dsh >= 0.1.0-rc.7). Registered through a NESTED\n // inject on purpose: naming settingsScope in the module-level `inject`\n // would keep this whole plugin unmounted on any host without that\n // service — the market's own page would vanish on rc.6 to gain a card\n // rc.6 cannot render. Nested, the card simply never appears there.\n const settingsCtx = ctx as unknown as {\n inject(services: string[], callback: (scoped: SettingsScopeHost) => void): void\n }\n settingsCtx.inject(['settingsScope'], (scoped) => {\n scoped.slots.inject('settings.plugin.item', () => scoped.slots.register({\n name: 'settings.plugin.item',\n key: NS,\n locale: NS,\n inject: () => ({ t }),\n }, () => h(SettingsCard, { t, onRemoved: () => { const off = retireSection; retireSection = null; off?.() } })))\n })\n\n const Toast = () => h(InstallToast, { t })\n ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'dsh-market-toast',\n label: () => 'dsh-market',\n }, Toast))\n}\n"],"mappings":"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"} \ No newline at end of file diff --git a/packages/dshmarket/cordis.patch.yml b/packages/dshmarket/cordis.patch.yml new file mode 100644 index 000000000..156434738 --- /dev/null +++ b/packages/dshmarket/cordis.patch.yml @@ -0,0 +1,4 @@ +# dsh bundle patch: inserts this plugin into a profile's layer stack. +- insert: + - id: dsh-market + name: 'dshmarket' diff --git a/packages/dshmarket/data/registry-snapshot.json b/packages/dshmarket/data/registry-snapshot.json new file mode 100644 index 000000000..cc9bdee45 --- /dev/null +++ b/packages/dshmarket/data/registry-snapshot.json @@ -0,0 +1,41140 @@ +{ + "name": "awesome-dsh-plugin", + "url": "https://awesome-dsh-plugin.com", + "source": "https://github.com/awesome-dsh-plugin/awesome-dsh-plugin", + "updated": "2026-08-28", + "count": 2452, + "categories": { + "agi": { + "en": "AGI Architecture Exploration", + "zh": "AGI 架构探索" + }, + "ui": { + "en": "UI Enhancements", + "zh": "UI 增强" + }, + "usage": { + "en": "Usage & Billing", + "zh": "用量与计费" + }, + "theme": { + "en": "Themes & Appearance", + "zh": "主题与外观" + }, + "model": { + "en": "Models & Providers", + "zh": "模型与账号接入" + }, + "identity": { + "en": "Identity & Communication", + "zh": "身份与通信" + }, + "session": { + "en": "Sessions & Messages", + "zh": "会话与消息" + }, + "memory": { + "en": "Memory", + "zh": "记忆" + }, + "tools": { + "en": "Tools & Capabilities", + "zh": "工具与能力" + }, + "browser": { + "en": "Browser & Web", + "zh": "浏览器与网页" + }, + "vision": { + "en": "Vision & Multimodal", + "zh": "视觉与多模态" + }, + "voice": { + "en": "Voice & Audio", + "zh": "语音与音频" + }, + "docs": { + "en": "Docs & Rendering", + "zh": "文档与渲染" + }, + "skill": { + "en": "Skills", + "zh": "技能包" + }, + "workflow": { + "en": "Workflow & Automation", + "zh": "工作流与自动化" + }, + "git": { + "en": "Git & Code Review", + "zh": "Git 与代码评审" + }, + "notify": { + "en": "Notifications & Integrations", + "zh": "通知与集成" + }, + "dev": { + "en": "Development & Runtime", + "zh": "开发与运行时" + }, + "security": { + "en": "Security & Permissions", + "zh": "安全与权限" + }, + "remote": { + "en": "Remote & Mobile", + "zh": "远程与移动端" + }, + "market": { + "en": "Plugin Markets & Managers", + "zh": "插件市场与管理" + }, + "fun": { + "en": "Just for Fun", + "zh": "娱乐" + } + }, + "plugins": [ + { + "name": "dsh-memory", + "owner": "FuRongJun-1999", + "url": "https://github.com/FuRongJun-1999/dsh-memory", + "page": "https://awesome-dsh-plugin.com/p/FuRongJun-1999/dsh-memory/", + "category": "agi", + "description": { + "en": "White-box AGI architecture exploration: metacognition (self-cognition loop), continual learning (knowledge flywheel), world model (condition space, spatiotemporal memory graph), self-improvement (bootstrap discipline), zero-LLM white-box pipeline, and auditable trust guardrails.", + "zh": "白箱AGI架构探索:元认知(自我认知循环)、持续学习(知识飞轮)、世界模型(条件空间+语义时空图)、自我改进(自举纪律)、零LLM白箱管线与可审计信任护栏。" + }, + "npm": "@furongjun1999/dsh-memory", + "tarball": "https://github.com/FuRongJun-1999/dsh-memory/releases/download/v0.3.0/furongjun1999-dsh-memory-0.3.0.tgz", + "stars": 35, + "downloads": 1856, + "install": "dsh plugin --profile web add @furongjun1999/dsh-memory", + "added": "2026-08-14" + }, + { + "name": "dsh-status-rotator", + "owner": "01Virex", + "url": "https://github.com/01Virex/dsh-status-rotator", + "page": "https://awesome-dsh-plugin.com/p/01Virex/dsh-status-rotator/", + "category": "ui", + "description": { + "en": "Replaces the \"Deep diving...\" turn-status label with rotating meme-worthy phrases, with typewriter and gradient effects.", + "zh": "把回合状态那句 \"Deep diving...\" 替换成更有梗的自定义文案,按阶段轮换,支持打字机与流动渐变。" + }, + "npm": "dsh-status-rotator", + "stars": 55, + "downloads": 2436, + "install": "dsh plugin --profile web add dsh-status-rotator", + "added": "2026-08-14" + }, + { + "name": "dsh-workspace-menu", + "owner": "0imzero", + "url": "https://github.com/0imzero/dsh-workspace-menu", + "page": "https://awesome-dsh-plugin.com/p/0imzero/dsh-workspace-menu/", + "category": "ui", + "description": { + "en": "Workspace/chat context menu for the DSH home page: pin, rename, open in file explorer, archive, fork, copy, and open in a new window.", + "zh": "DSH 主页工作区/会话增强菜单:置顶、重命名、资源管理器打开、归档、分叉、复制、新窗口打开。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:0imzero/dsh-workspace-menu", + "added": "2026-08-26" + }, + { + "name": "dsh-spotlight", + "owner": "0xsline", + "url": "https://github.com/0xsline/dsh-spotlight", + "page": "https://awesome-dsh-plugin.com/p/0xsline/dsh-spotlight/", + "category": "ui", + "description": { + "en": "Keyboard-first command palette for the DSH Web UI.", + "zh": "键盘优先的命令面板(command palette)。" + }, + "npm": null, + "stars": 17, + "downloads": null, + "install": "dsh plugin --profile web add github:0xsline/dsh-spotlight", + "added": "2026-08-13" + }, + { + "name": "dph-taskboard", + "owner": "1070296335-create", + "url": "https://github.com/1070296335-create/dph-taskboard", + "page": "https://awesome-dsh-plugin.com/p/1070296335-create/dph-taskboard/", + "category": "ui", + "description": { + "en": "Session-based task board in the sidebar: drag sessions into todo/doing/review/done columns, create sessions with model and reasoning-effort selection, trash with restore, notes, export/import.", + "zh": "侧边栏会话化任务看板:拖拽会话到待办/进行中/评审中/已完成四列,新建会话可选模型与推理强度,回收站可恢复,支持备注与导出导入。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:1070296335-create/dph-taskboard", + "added": "2026-08-19" + }, + { + "name": "dsh-composer-expand", + "owner": "13071301808", + "url": "https://github.com/13071301808/dsh-composer-expand", + "page": "https://awesome-dsh-plugin.com/p/13071301808/dsh-composer-expand/", + "category": "ui", + "description": { + "en": "Composer expand/collapse toggle for the Web UI: a ⬆/⬇ button in the composer tool row grows the input to a tall 70vh writing view for long drafts.", + "zh": "Web UI 输入框展开/收起:composer 工具行新增 ⬆/⬇ 按钮,一键把输入框扩大到 70vh 高度,方便写长 prompt。" + }, + "npm": "dsh-composer-expand", + "stars": 6, + "downloads": 888, + "install": "dsh plugin --profile web add dsh-composer-expand", + "added": "2026-08-15" + }, + { + "name": "dsh-wsl-open", + "owner": "173787247", + "url": "https://github.com/173787247/dsh-wsl-open", + "page": "https://awesome-dsh-plugin.com/p/173787247/dsh-wsl-open/", + "category": "ui", + "description": { + "en": "Opens WSL Linux paths from DeepSeek Harness chat in the Windows default app or Explorer.", + "zh": "把 DeepSeek Harness 聊天里的 WSL Linux 路径在 Windows 默认程序或资源管理器中打开。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:173787247/dsh-wsl-open", + "added": "2026-08-28" + }, + { + "name": "dsh-effort-slider", + "owner": "2768651338", + "url": "https://github.com/2768651338/dsh-effort-slider", + "page": "https://awesome-dsh-plugin.com/p/2768651338/dsh-effort-slider/", + "category": "ui", + "description": { + "en": "A Claude Code-style reasoning-effort slider for the DSH Web UI: stepless drag, snap-on-release, a WebGL fire trail, and real, working thinking-effort control for any custom third-party model or provider.", + "zh": "仿 Claude Code 的推理等级滑块:无极拖动、松手吸附、WebGL 火焰跟随,任何自定义第三方模型/提供商都能获得真实生效的思考强度调节。" + }, + "npm": null, + "stars": 13, + "downloads": null, + "install": "dsh plugin --profile web add github:2768651338/dsh-effort-slider", + "added": "2026-08-17" + }, + { + "name": "dsh-plugin-open-app", + "owner": "2nd1st", + "url": "https://github.com/2nd1st/dsh-plugin-open-app", + "page": "https://awesome-dsh-plugin.com/p/2nd1st/dsh-plugin-open-app/", + "category": "ui", + "description": { + "en": "Runs open-mcp-apps inside DSH: every MCP app gets its own sidebar container with a separate workspace, session, and App mode, an agent status strip under the app, and inline app rendering in ordinary chats.", + "zh": "把 open-mcp-apps 接进 DSH:每个 MCP app 都是侧边栏里自己的容器,带独立 workspace、会话与 App mode,应用下方是 agent 状态条,普通聊天里也能行内渲染 app。" + }, + "npm": "@2nd1st/dsh-plugin-open-app", + "stars": 5, + "downloads": 407, + "install": "dsh plugin --profile web add @2nd1st/dsh-plugin-open-app", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/2nd1st/dsh-plugin-open-app/main/docs/screenshot-app-container.jpg", + "https://raw.githubusercontent.com/2nd1st/dsh-plugin-open-app/main/docs/screenshot-inline.jpg" + ] + }, + { + "name": "chicheng-quickinput", + "owner": "534119219", + "url": "https://github.com/534119219/chicheng-quickinput", + "page": "https://awesome-dsh-plugin.com/p/534119219/chicheng-quickinput/", + "category": "ui", + "description": { + "en": "Vault button beside the DSH composer: save keys, servers, phone numbers, addresses and URLs, auto-collected from conversations, password-protected with WebDAV backup; click to insert, Ctrl+click to send.", + "zh": "输入栏旁的保险箱按钮:保存密钥、服务器、手机号、地址与网址,自动识别会话中的敏感信息提醒收录,密码保护并支持 WebDAV 备份;点击填入输入框,Ctrl+点击直接发送。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:534119219/chicheng-quickinput", + "added": "2026-08-23" + }, + { + "name": "dsh-chat-width", + "owner": "764475881", + "url": "https://github.com/764475881/dsh-chat-width", + "page": "https://awesome-dsh-plugin.com/p/764475881/dsh-chat-width/", + "category": "ui", + "description": { + "en": "Drag-to-resize the chat content width in the DeepSeek Harness web UI: a handle on the column right edge overrides the built-in 748px width (default 1040px, 520-2400 range, persisted per browser, double-click resets).", + "zh": "为 DeepSeek Harness 网页界面提供可拖拽的聊天内容宽度:文字列右缘的把手覆盖内置的 748px 宽度(默认 1040px、范围 520~2400、按浏览器记忆,双击恢复默认)。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:764475881/dsh-chat-width", + "added": "2026-08-24" + }, + { + "name": "dsh-model-search", + "owner": "a1073097082", + "url": "https://github.com/a1073097082/dsh-model-search", + "page": "https://awesome-dsh-plugin.com/p/a1073097082/dsh-model-search/", + "category": "ui", + "description": { + "en": "Adds searchable filtering to the model selector by provider, model name, and model ID; spaces act as wildcards.", + "zh": "为模型选择器增加按提供方、模型名称和模型 ID 搜索的筛选框,空格表示任意字符。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:a1073097082/dsh-model-search", + "added": "2026-08-15" + }, + { + "name": "dsh-auto-collapse", + "owner": "a179-sanae", + "url": "https://github.com/a179-sanae/dsh-auto-collapse", + "page": "https://awesome-dsh-plugin.com/p/a179-sanae/dsh-auto-collapse/", + "category": "ui", + "description": { + "en": "Codex-style workflow auto-collapse: finished turns fold into a single \"processed in Xs\" row leaving only the final answer; tool calls and think blocks collapse into live-summary chips that follow streaming; expand level by level, fully reversible on uninstall.", + "zh": "Codex 风格工作流自动折叠:回合完成收成一行「已处理 X秒」只留最终正文,工具/思考块折叠成实时摘要 chip(运行中跟随流式滚动),逐级点击展开;卸载完整还原。" + }, + "npm": null, + "stars": 50, + "downloads": null, + "install": "dsh plugin --profile web add github:a179-sanae/dsh-auto-collapse", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/a179-sanae/dsh-auto-collapse/main/assets/screenshot.png" + ] + }, + { + "name": "dsh-skill-picker", + "owner": "a735624258", + "url": "https://github.com/a735624258/dsh-skill-picker", + "page": "https://awesome-dsh-plugin.com/p/a735624258/dsh-skill-picker/", + "category": "ui", + "description": { + "en": "WorkBuddy-style skill picker: a button beside the composer opens a searchable list of installed skills; picking one inserts the official /skill-name gesture, so the skill loads with your message.", + "zh": "WorkBuddy 同款技能选择器:输入框旁 ⚡ 按钮弹出全部技能列表,支持搜索与最近/常用排序,点选即插入官方 `/技能名` 手势,随消息发出自动加载执行。" + }, + "npm": "dsh-skill-picker", + "stars": 25, + "downloads": 1224, + "install": "dsh plugin --profile web add dsh-skill-picker", + "added": "2026-08-16" + }, + { + "name": "dsh-file-mentions", + "owner": "a903067276-rgb", + "url": "https://github.com/a903067276-rgb/dsh-file-mentions", + "page": "https://awesome-dsh-plugin.com/p/a903067276-rgb/dsh-file-mentions/", + "category": "ui", + "description": { + "en": "Clickable file paths in DSH replies: Codex-style inline open, reveal in file manager, and a mentioned-files chip list at the turn tail.", + "zh": "DSH 回复中的文件路径可点击:Codex 风格行内打开、文件管理器定位、回合尾部文件 chip 列表。" + }, + "npm": null, + "stars": 12, + "downloads": null, + "install": "dsh plugin --profile web add github:a903067276-rgb/dsh-file-mentions", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/a903067276-rgb/dsh-file-mentions/HEAD/assets/screenshot.png" + ] + }, + { + "name": "dsh-file-upload", + "owner": "a903067276-rgb", + "url": "https://github.com/a903067276-rgb/dsh-file-upload", + "page": "https://awesome-dsh-plugin.com/p/a903067276-rgb/dsh-file-upload/", + "category": "ui", + "description": { + "en": "One upload button plus drag-and-drop files into the conversation as local paths: save to the project's uploads/, path text into the input box, works with any vision tool.", + "zh": "统一上传按钮 + 拖拽文件直进对话:文件保存到项目 uploads/,路径文本自动进输入框,可配合任意视觉工具看图。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:a903067276-rgb/dsh-file-upload", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/a903067276-rgb/dsh-file-upload/HEAD/assets/screenshot.png" + ] + }, + { + "name": "dsh-hud", + "owner": "a903067276-rgb", + "url": "https://github.com/a903067276-rgb/dsh-hud", + "page": "https://awesome-dsh-plugin.com/p/a903067276-rgb/dsh-hud/", + "category": "ui", + "description": { + "en": "HUD status panel: Git status, MCP servers, skills, model and token usage in a floating side panel.", + "zh": "HUD 状态面板:Git 状态、MCP 服务器、技能列表、模型与 token 用量,悬浮侧栏一览无余。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:a903067276-rgb/dsh-hud", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/a903067276-rgb/dsh-hud/HEAD/assets/hud-panel.png" + ] + }, + { + "name": "dsh-plan-switch", + "owner": "a903067276-rgb", + "url": "https://github.com/a903067276-rgb/dsh-plan-switch", + "page": "https://awesome-dsh-plugin.com/p/a903067276-rgb/dsh-plan-switch/", + "category": "ui", + "description": { + "en": "One-click enter/exit Plan mode for the DSH web input bar, a quick-click shortcut for /plan.", + "zh": "输入框一键进/出 Plan 模式(/plan 的快捷点击),常驻小按钮。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:a903067276-rgb/dsh-plan-switch", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/a903067276-rgb/dsh-plan-switch/HEAD/assets/plan-button.png" + ] + }, + { + "name": "dsh-question-nav", + "owner": "AbelKeithsun", + "url": "https://github.com/AbelKeithsun/dsh-question-nav", + "page": "https://awesome-dsh-plugin.com/p/AbelKeithsun/dsh-question-nav/", + "category": "ui", + "description": { + "en": "In-session question navigator for the DSH Web UI: a dot minimap on the conversation edge (left or right, configurable in settings), one dot per user question — hover shows the full question text, click jumps to it.", + "zh": "DSH Web UI 会话内提问导航:对话栏左/右缘(可在设置中切换)的圆点迷你地图,一个圆点对应一个用户提问——悬停显示全文,点击跳转到该提问。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:AbelKeithsun/dsh-question-nav", + "added": "2026-08-27" + }, + { + "name": "dsh-web-mobile-fix", + "owner": "AcidGr", + "url": "https://github.com/AcidGr/dsh-web-mobile-fix", + "page": "https://awesome-dsh-plugin.com/p/AcidGr/dsh-web-mobile-fix/", + "category": "ui", + "description": { + "en": "Mobile layout fixes for the Web UI on narrow screens: full-screen settings panel, one-row plugin nav, full-screen sidebar, centered popups, icon-only session-log button.", + "zh": "Web UI 移动端布局修复:窄屏下设置面板全屏化、插件导航单行排满、侧边栏全屏、弹层居中、会话日志按钮图标化。" + }, + "npm": "dsh-web-mobile-fix", + "stars": 5, + "downloads": 1088, + "install": "dsh plugin --profile web add dsh-web-mobile-fix", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/AcidGr/dsh-web-mobile-fix/main/assets/screenshot-1.jpg", + "https://raw.githubusercontent.com/AcidGr/dsh-web-mobile-fix/main/assets/screenshot-2.jpg" + ] + }, + { + "name": "dsh-docking-layout", + "owner": "ai-eks", + "url": "https://github.com/ai-eks/dsh-docking-layout", + "page": "https://awesome-dsh-plugin.com/p/ai-eks/dsh-docking-layout/", + "category": "ui", + "description": { + "en": "Dockable DSH Web UI layout that organizes conversations as tabs across draggable split groups.", + "zh": "可停靠的 DSH Web UI 布局插件,将对话组织为标签页,并支持拖拽拆分对话分组。" + }, + "npm": "@ai-eks/dsh-docking-layout", + "stars": 2, + "downloads": 227, + "install": "dsh plugin --profile web add @ai-eks/dsh-docking-layout", + "added": "2026-08-24" + }, + { + "name": "dsh-anchored-monitor", + "owner": "Aik358", + "url": "https://github.com/Aik358/dsh-anchored-monitor", + "page": "https://awesome-dsh-plugin.com/p/Aik358/dsh-anchored-monitor/", + "category": "ui", + "description": { + "en": "A whip for DeepSeek V4 Pro: it watches the reasoning fingerprint of every thinking block and pulls the model back when it slips from focused \"We will / I will\" mode into scattered \"let me\" mode.", + "zh": "给 DeepSeek V4 Pro 的鞭子:实时监听每个思维块的指纹,当模型从专注的 We will / I will 模式滑向发散的 let me 模式时把它拉回来。" + }, + "npm": "@a9i5k4/dsh-anchored-monitor", + "stars": 11, + "downloads": 1946, + "install": "dsh plugin --profile web add @a9i5k4/dsh-anchored-monitor", + "added": "2026-08-18" + }, + { + "name": "dsh-alive", + "owner": "AikenFra", + "url": "https://github.com/AikenFra/dsh-alive", + "page": "https://awesome-dsh-plugin.com/p/AikenFra/dsh-alive/", + "category": "ui", + "description": { + "en": "Zero-token online status indicator for the DeepSeek Harness web UI: an always-visible online/offline dot in the conversation header, auto-refreshed every 15 seconds with no LLM calls.", + "zh": "零 token 在线状态指示器:会话头部常驻显示 ● 在线 / ● 离线 状态点,每 15 秒自动检测一次,不调用任何 LLM。" + }, + "npm": "dsh-alive", + "stars": 2, + "downloads": 409, + "install": "dsh plugin --profile web add dsh-alive", + "added": "2026-08-16" + }, + { + "name": "dsh-drag-and-drop", + "owner": "AKIRACOD", + "url": "https://github.com/AKIRACOD/dsh-drag-and-drop", + "page": "https://awesome-dsh-plugin.com/p/AKIRACOD/dsh-drag-and-drop/", + "category": "ui", + "description": { + "en": "File-drag fork: drop documents as removable chips above the composer, send without typing.", + "zh": "拖放 fork:文档以可删除「文件芯片」挂在输入框上方,不打字也能发送。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:AKIRACOD/dsh-drag-and-drop", + "added": "2026-08-14" + }, + { + "name": "dock", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/dock", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/dock/", + "category": "ui", + "description": { + "en": "VSCode-style workbench shell for the DSH Web UI: activity bar, side bar, editor area, panel and status bar, with a registry service (ctx.workbench) for mounting panels, views and commands.", + "zh": "DSH Web 工作台基座插件:提供 VSCode 风格布局外壳(活动栏/侧边栏/编辑器区/面板/状态栏)和 ctx.workbench 开放注册表,供功能插件挂载面板、视图与命令。" + }, + "npm": "dock-base", + "stars": 2, + "downloads": 148, + "install": "dsh plugin --profile web add dock-base", + "added": "2026-08-22" + }, + { + "name": "dock-editor", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/dock-editor", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/dock-editor/", + "category": "ui", + "description": { + "en": "Text viewer and editor for the dock workbench: undo/redo, Ctrl+S save, unsaved-change confirmation and binary detection, registered as the default file viewer.", + "zh": "dock 系列文本查看与编辑器:支持撤销重做、Ctrl+S 保存、未保存确认和二进制检测,作为 dock-files 的默认文本查看器。" + }, + "npm": "dock-editor", + "stars": 1, + "downloads": 325, + "install": "dsh plugin --profile web add dock-editor", + "added": "2026-08-22" + }, + { + "name": "dock-files", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/dock-files", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/dock-files/", + "category": "ui", + "description": { + "en": "File explorer for the dock workbench: tree browsing of the session workspace with create/rename/copy-paste/delete, drag-and-drop import and paste of local files and clipboard images.", + "zh": "dock 系列文件浏览插件:侧边栏目录树浏览会话工作区,支持新建/重命名/复制粘贴/删除、拖放导入、粘贴本地文件与剪贴板图片。" + }, + "npm": "dock-files", + "stars": 1, + "downloads": 177, + "install": "dsh plugin --profile web add dock-files", + "added": "2026-08-22" + }, + { + "name": "dock-git", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/dock-git", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/dock-git/", + "category": "ui", + "description": { + "en": "Git history visualization for the dock workbench: swimlane commit graph, branch and tag management, stage/commit/push and remote operations.", + "zh": "dock 系列 Git 历史可视化插件:泳道式提交图、分支/标签管理、暂存/提交/推送与远端操作。" + }, + "npm": "dock-git", + "stars": 1, + "downloads": 313, + "install": "dsh plugin --profile web add dock-git", + "added": "2026-08-22" + }, + { + "name": "dock-images", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/dock-images", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/dock-images/", + "category": "ui", + "description": { + "en": "Image viewer for the dock workbench: PNG, JPEG, GIF, WebP, BMP, SVG, ICO and AVIF, with SVG rendered as image data only.", + "zh": "dock 系列图片查看器:支持 PNG/JPEG/GIF/WebP/BMP/SVG/ICO/AVIF 格式,SVG 仅以安全方式渲染。" + }, + "npm": "dock-images", + "stars": 1, + "downloads": 174, + "install": "dsh plugin --profile web add dock-images", + "added": "2026-08-22" + }, + { + "name": "dock-markdown", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/dock-markdown", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/dock-markdown/", + "category": "ui", + "description": { + "en": "Markdown viewer for the dock workbench: GFM rendering with DOMPurify sanitization, document outline and relative-path image/link resolution, with one-click switch to editing.", + "zh": "dock 系列 Markdown 查看器:GFM 渲染 + DOMPurify 消毒、文档大纲、相对路径图片/链接解析,可一键切换编辑。" + }, + "npm": "dock-markdown", + "stars": 1, + "downloads": 173, + "install": "dsh plugin --profile web add dock-markdown", + "added": "2026-08-22" + }, + { + "name": "dsh-sysmon", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/dsh-sysmon", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/dsh-sysmon/", + "category": "ui", + "description": { + "en": "System status floating widget: live CPU/memory/disk usage in the bottom-right corner of the DSH Web UI with threshold colour warnings.", + "zh": "DSH Web 右下角系统状态悬浮窗:实时显示 CPU、内存、磁盘占用率,带阈值变色告警。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:AKS1st/dsh-sysmon", + "added": "2026-08-15" + }, + { + "name": "dsh-custom-plugin", + "owner": "AlexPeng07", + "url": "https://github.com/AlexPeng07/dsh-custom-plugin", + "page": "https://awesome-dsh-plugin.com/p/AlexPeng07/dsh-custom-plugin/", + "category": "ui", + "description": { + "en": "Web GUI convenience suite with appearance controls, timeline navigation, project folders, prompts, conversation export, Mermaid rendering, quote reply, and DeepSeek balance and daily token usage.", + "zh": "DSH Web GUI 便利套件,提供外观控制、时间线导航、项目文件夹、提示词、会话导出、Mermaid 渲染、引用回复,以及 DeepSeek 余额和每日 token 用量。" + }, + "npm": "@alexpeng/dsh-custom-plugin", + "stars": 3, + "downloads": 731, + "install": "dsh plugin --profile web add @alexpeng/dsh-custom-plugin", + "added": "2026-08-24" + }, + { + "name": "dsh-ui-enhancer", + "owner": "AlexYin-Tongji", + "url": "https://github.com/AlexYin-Tongji/dsh-ui-enhancer", + "page": "https://awesome-dsh-plugin.com/p/AlexYin-Tongji/dsh-ui-enhancer/", + "category": "ui", + "description": { + "en": "Desktop-style UI enhancements for the DSH Web client: adaptive wallpapers, a desktop pet companion with an activity feed and feeding economy, @file workspace references, and the resizable file/preview right panel.", + "zh": "面向 DSH Web 客户端的桌面化 UI 增强:自适应对比度壁纸、带活动流与投喂玩法的桌面宠物、@file 工作区文件引用,以及可缩放的文件/预览右侧面板。" + }, + "npm": "dsh-ui-enhancer", + "stars": 1, + "downloads": 451, + "install": "dsh plugin --profile web add dsh-ui-enhancer", + "added": "2026-08-20" + }, + { + "name": "dsh-thumb", + "owner": "AliceLJY", + "url": "https://github.com/AliceLJY/dsh-thumb", + "page": "https://awesome-dsh-plugin.com/p/AliceLJY/dsh-thumb/", + "category": "ui", + "description": { + "en": "A phone shell for the web GUI - the sidebar becomes an overlay drawer that closes when a session is tapped, the settings sheet goes full-screen single column, and the transcript is retuned for phone reading.", + "zh": "网页版界面的手机外壳:侧栏改为覆盖式抽屉、点会话自动收起,设置面板改全屏单列,正文排版按手机阅读距离重调。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:AliceLJY/dsh-thumb", + "added": "2026-08-27" + }, + { + "name": "ui-status-label", + "owner": "alingalingling", + "url": "https://github.com/alingalingling/ui-status-label", + "page": "https://awesome-dsh-plugin.com/p/alingalingling/ui-status-label/", + "category": "ui", + "description": { + "en": "Customize the \"deep diving\" thinking status label to anything you like.", + "zh": "把鲸鱼娘思考时的 \"deep diving\" 状态文案自定义成任意你想要的样子。" + }, + "npm": null, + "stars": 40, + "downloads": null, + "install": "dsh plugin --profile web add github:alingalingling/ui-status-label", + "added": "2026-08-13" + }, + { + "name": "dsh-plugin-video-player", + "owner": "anitman", + "url": "https://github.com/anitman/dsh-plugin-video-player", + "page": "https://awesome-dsh-plugin.com/p/anitman/dsh-plugin-video-player/", + "category": "ui", + "description": { + "en": "TikTok-style video feed in a floating sidebar window: swipe local or SMB-NAS folders, or paste online video URLs (yt-dlp, bilibili/YouTube, cookies.txt login state); the window auto-fits the video aspect ratio, with fullscreen, drag-resize, Range streaming and an agent video-queue endpoint.", + "zh": "侧栏浮动窗口里的抖音式刷视频:本地/SMB NAS 文件夹上下滑动,或粘贴在线视频地址(yt-dlp 解析,支持 bilibili/YouTube 与 cookies.txt 登录态);窗口随视频宽高比自适应,支持全屏、拖动缩放、Range 流式播放,并提供 agent 视频推送队列端点。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:anitman/dsh-plugin-video-player", + "added": "2026-08-19" + }, + { + "name": "dsh-desk-pet", + "owner": "anneheartrecord", + "url": "https://github.com/anneheartrecord/dsh-desk-pet", + "page": "https://awesome-dsh-plugin.com/p/anneheartrecord/dsh-desk-pet/", + "category": "ui", + "description": { + "en": "macOS desk pet that lives outside the DSH page: a real always-on-top window that clears fullscreen Spaces, six states driven by local DSH, a native right-click menu with quiet mode and a session list, and a bundled skill that turns one photo into a full eighteen-pose skin on your own image tool and credentials; system Python and ctypes, no Electron and no dependencies.", + "zh": "装在 DSH 页面之外的 macOS 桌宠:一个真的置顶窗口,全屏 Space 也盖得住;六种状态跟着本地 DSH 走,原生右键菜单里有免打扰与会话清单;自带的 skill 用你自己的画图工具和额度,把一张照片变成整套十八个姿势的皮肤。跑系统自带的 Python 与 ctypes,不用 Electron,零依赖。" + }, + "npm": "deepseek-desk-pet", + "stars": 5, + "downloads": 422, + "install": "dsh plugin --profile web add deepseek-desk-pet", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/anneheartrecord/dsh-desk-pet/main/docs/media/diy-skin.png", + "https://raw.githubusercontent.com/anneheartrecord/dsh-desk-pet/main/docs/media/floats-above.png", + "https://raw.githubusercontent.com/anneheartrecord/dsh-desk-pet/main/docs/media/skins.png", + "https://raw.githubusercontent.com/anneheartrecord/dsh-desk-pet/main/docs/media/states.png" + ] + }, + { + "name": "DragView", + "owner": "anzhaohao", + "url": "https://github.com/anzhaohao/DragView", + "page": "https://awesome-dsh-plugin.com/p/anzhaohao/DragView/", + "category": "ui", + "description": { + "en": "Drag-and-drop file attachments as Codex-style cards, with in-app PDF/text/video/audio previews and system-default opening for other formats.", + "zh": "拖拽文件以 Codex 风格附件卡片呈现,支持 PDF/文本/音视频应用内预览,其余格式调用系统默认应用打开。" + }, + "npm": "dsh-dragview", + "stars": 0, + "downloads": 0, + "install": "dsh plugin --profile web add dsh-dragview", + "added": "2026-08-25" + }, + { + "name": "dsh-WallpaperAndCost", + "owner": "AppliedYuu", + "url": "https://github.com/AppliedYuu/dsh-WallpaperAndCost", + "page": "https://awesome-dsh-plugin.com/p/AppliedYuu/dsh-WallpaperAndCost/", + "category": "ui", + "description": { + "en": "Merged plugin: wallpaper customization (original-resolution uploads, crop editor, per-area opacity, MP4/WebM dynamic wallpapers) + Steam Workshop wallpaper extraction (parses scene.pkg/.tex via a bundled Python script, preview/download) + DeepSeek balance & usage widget + OpenCode Go plan usage (rolling/weekly/monthly percents in a top header pill; expand for progress bars & reset countdowns). API keys are read from DSH's credential store by the host — never stored in source or the browser.", + "zh": "合并插件:壁纸定制(原图不压缩、裁剪编辑器、左/中/右分区不透明度、MP4/WebM 动态壁纸)+ Steam 创意工坊壁纸提取(内置 scripts/extract_scene_pkg.py 解析 scene.pkg/.tex,支持预览/下载)+ DeepSeek 余额与用量小组件 + OpenCode Go 套餐用量(会话顶部药丸按钮显示滚动/周/月用量百分比,点开展开进度条与重置倒计时)。各 API key 均由 host 从 DSH 凭据库读取,不入源码/浏览器。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:AppliedYuu/dsh-WallpaperAndCost", + "added": "2026-08-19" + }, + { + "name": "dsh-multi-lang-ui", + "owner": "asd13006", + "url": "https://github.com/asd13006/dsh-multi-lang-ui", + "page": "https://awesome-dsh-plugin.com/p/asd13006/dsh-multi-lang-ui/", + "category": "ui", + "description": { + "en": "Add six languages (繁體中文, 日本語, 한국어, Français, Deutsch, Español) to the DeepSeek Harness Web UI language options, with hand-polished per-language translations and automatic fallbacks (Simplified-to-Traditional conversion for zh-TW, English for the others) covering upstream updates and third-party plugins.", + "zh": "為 DeepSeek Harness Web UI 語言選單新增六種語言(繁體中文、日本語、한국어、Français、Deutsch、Español):各語言逐條精譯,缺字串自動兜底(繁中即時簡轉繁、其他語言回退英文),官方更新與第三方插件都能覆蓋。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:asd13006/dsh-multi-lang-ui", + "added": "2026-08-18" + }, + { + "name": "dsh-message-preview", + "owner": "asukasec", + "url": "https://github.com/asukasec/dsh-message-preview", + "page": "https://awesome-dsh-plugin.com/p/asukasec/dsh-message-preview/", + "category": "ui", + "description": { + "en": "Right-edge user-message navigator with an adaptive block layout that fits the available height, plus hover previews, keyboard controls, and click-to-jump navigation.", + "zh": "右侧用户消息导航条,根据消息数量与可用高度自适应排布导航块,并支持悬停预览、键盘操作与点击跳转。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:asukasec/dsh-message-preview", + "added": "2026-08-14" + }, + { + "name": "dsh-sleep-send", + "owner": "Awu12277", + "url": "https://github.com/Awu12277/dsh-sleep-send", + "page": "https://awesome-dsh-plugin.com/p/Awu12277/dsh-sleep-send/", + "category": "ui", + "description": { + "en": "Schedule-send button and panel to the right of the composer, with smart time slots, a custom date/time picker, several queued sends at once, and localStorage persistence that restores pending tasks after a page reload.", + "zh": "在输入框右侧提供「定时发送」按钮与配置面板,支持智能时段、自定义日期时间、多个定时任务并行,任务经 localStorage 持久化,刷新页面后自动恢复。" + }, + "npm": "dsh-sleep-send", + "stars": 4, + "downloads": 930, + "install": "dsh plugin --profile web add dsh-sleep-send", + "added": "2026-08-18" + }, + { + "name": "dsh-trail#bundle", + "owner": "ayahunter", + "url": "https://github.com/ayahunter/dsh-trail/tree/main/packages/bundle", + "page": "https://awesome-dsh-plugin.com/p/ayahunter/dsh-trail--packages-bundle/", + "category": "ui", + "description": { + "en": "Replaces the Web GUI trajectory tab with a new-user-friendly round-by-round storyline, plain-language tool labels, category filters and fuzzy search.", + "zh": "把 Web GUI 的轨迹页签替换为新手友好的回合故事线:工具名通俗中文化,支持类别筛选与模糊搜索。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:ayahunter/dsh-trail#path:/packages/bundle", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/ayahunter/dsh-trail/main/docs/screenshot.png", + "https://raw.githubusercontent.com/ayahunter/dsh-trail/main/docs/screenshots/rounds-view.png", + "https://raw.githubusercontent.com/ayahunter/dsh-trail/main/docs/screenshots/tool-details.png" + ] + }, + { + "name": "dsh-web-launcher", + "owner": "ayingQAQ", + "url": "https://github.com/ayingQAQ/dsh-web-launcher", + "page": "https://awesome-dsh-plugin.com/p/ayingQAQ/dsh-web-launcher/", + "category": "ui", + "description": { + "en": "Windows desktop launcher bundle for DSH Web that creates a managed shortcut, then restarts DSH in the background and refreshes the existing browser page.", + "zh": "DSH Web 的 Windows 桌面启动器插件,创建受管理的快捷方式,后续启动会在后台重启 DSH 并刷新已有浏览器页面。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:ayingQAQ/dsh-web-launcher", + "added": "2026-08-18" + }, + { + "name": "easy-archive", + "owner": "bainianlaoyao", + "url": "https://github.com/bainianlaoyao/easy-archive", + "page": "https://awesome-dsh-plugin.com/p/bainianlaoyao/easy-archive/", + "category": "ui", + "description": { + "en": "Two-step inline archive on workspace sidebar rows — one click arms a red confirm, the second click archives; the archive entry is removed from the ⋮ menu.", + "zh": "工作区侧边栏行内两击归档:点一次变红确认,再点即归档,归档项不再出现在 ⋮ 菜单里。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:bainianlaoyao/easy-archive", + "added": "2026-08-18" + }, + { + "name": "dsh-composer-history", + "owner": "baisama-cloud", + "url": "https://github.com/baisama-cloud/dsh-composer-history", + "page": "https://awesome-dsh-plugin.com/p/baisama-cloud/dsh-composer-history/", + "category": "ui", + "description": { + "en": "Command history for the Web UI composer: press Up/Down to cycle through previously sent instructions, then restore the draft at the end of the history.", + "zh": "Web UI 输入框命令历史:按 ↑/↓ 浏览此前发送的指令,回到历史末尾时恢复原草稿。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:baisama-cloud/dsh-composer-history", + "added": "2026-08-20" + }, + { + "name": "dsh-GreaterClarity-plugin", + "owner": "Baisbt", + "url": "https://github.com/Baisbt/dsh-GreaterClarity-plugin", + "page": "https://awesome-dsh-plugin.com/p/Baisbt/dsh-GreaterClarity-plugin/", + "category": "ui", + "description": { + "en": "Enhances the DSH web session view — one-click collapse of reasoning and tool chains, numbered history quick-jump, Markdown export, AI avatars.", + "zh": "增强 DSH Web 会话视图:一键折叠思考链与工具链、历史快速定位、Markdown 导出、AI 头像。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Baisbt/dsh-GreaterClarity-plugin", + "added": "2026-08-24" + }, + { + "name": "my-dsh-plugins#dsh-think-zh-expand", + "owner": "baosfeng", + "url": "https://github.com/baosfeng/my-dsh-plugins/tree/main/plugins/dsh-think-zh-expand", + "page": "https://awesome-dsh-plugin.com/p/baosfeng/my-dsh-plugins--plugins-dsh-think-zh-expand/", + "category": "ui", + "description": { + "en": "Thinking in Chinese: injects a system prompt so thoughts and replies stay in Chinese, expands the reasoning block by default with interactive collapse, and localizes UI labels (npm: `dsh-think-zh-expand`).", + "zh": "思考增强:system-prompt 注入让思考与回复强制中文;思考内容默认展开显示、可交互折叠;界面英文标签中文化(npm: `dsh-think-zh-expand`)。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:baosfeng/my-dsh-plugins#path:/plugins/dsh-think-zh-expand", + "added": "2026-08-27" + }, + { + "name": "dsh-conversation-anchors", + "owner": "biggerboy", + "url": "https://github.com/biggerboy/dsh-conversation-anchors", + "page": "https://awesome-dsh-plugin.com/p/biggerboy/dsh-conversation-anchors/", + "category": "ui", + "description": { + "en": "Codex-style left-gutter tick rail for each user turn, with hover preview and click-to-scroll.", + "zh": "对话区左侧 Codex 风格短横线锚点,悬停预览,点击跳转到对应轮次。" + }, + "npm": "@biggerboy123/dsh-conversation-anchors", + "stars": 2, + "downloads": 107, + "install": "dsh plugin --profile web add @biggerboy123/dsh-conversation-anchors", + "added": "2026-08-25" + }, + { + "name": "dsh-drag-and-drop", + "owner": "bill9109", + "url": "https://github.com/bill9109/dsh-drag-and-drop", + "page": "https://awesome-dsh-plugin.com/p/bill9109/dsh-drag-and-drop/", + "category": "ui", + "description": { + "en": "Cross-platform file drag-and-drop with raw path insertion, no file copying.", + "zh": "跨平台文件拖拽与原始路径插入,无需复制文件。" + }, + "npm": null, + "stars": 22, + "downloads": null, + "install": "dsh plugin --profile web add github:bill9109/dsh-drag-and-drop", + "added": "2026-08-13" + }, + { + "name": "dsh-github-picker", + "owner": "bitxeno", + "url": "https://github.com/bitxeno/dsh-github-picker", + "page": "https://awesome-dsh-plugin.com/p/bitxeno/dsh-github-picker/", + "category": "ui", + "description": { + "en": "Composer GitHub picker: click the GitHub icon at the bottom-right of the input box to search the workspace repository's issues and pull requests via the gh CLI, and insert the reference as a URL or @owner/repo#number mention.", + "zh": "输入框右下角 GitHub 图标打开当前仓库的 issue/PR 搜索弹窗,数据来自本机 gh CLI,点选即以 URL 或 @owner/repo#number 形式插入引用。" + }, + "npm": "dsh-github-picker", + "stars": 1, + "downloads": 843, + "install": "dsh plugin --profile web add dsh-github-picker", + "added": "2026-08-18" + }, + { + "name": "dsh-pet-sprite", + "owner": "BlackBearCC", + "url": "https://github.com/BlackBearCC/dsh-pet-sprite", + "page": "https://awesome-dsh-plugin.com/p/BlackBearCC/dsh-pet-sprite/", + "category": "ui", + "description": { + "en": "A pixel companion living in the chat area: hatches from an egg into one of three sprites or an LLM-drawn custom one from a text description, platform-jumps over message bubbles on its own, WASD controllable, chats in character through the models you already configured, and carries a nurture system (mood/power/health, levels, shop) driven by real token usage.", + "zh": "聊天区里的像素小宠物:从蛋孵化三选一,也可用一句话描述让 LLM 画出自定义形象,自动在消息气泡间平台跳跃,支持 WASD 操控与拖拽,用已配置的模型角色扮演陪聊,养成系统(心情/电量/健康、等级、商店)由真实 token 消耗驱动。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:BlackBearCC/dsh-pet-sprite", + "added": "2026-08-21" + }, + { + "name": "dsh-always-status-bar", + "owner": "Bobnemimimmi", + "url": "https://github.com/Bobnemimimmi/dsh-always-status-bar", + "page": "https://awesome-dsh-plugin.com/p/Bobnemimimmi/dsh-always-status-bar/", + "category": "ui", + "description": { + "en": "Keeps the native per-message status line always visible without hovering — date and time on every message, run duration, TTFT, and tokens per second on assistant messages.", + "zh": "原生消息状态栏无需悬停即可始终显示每条消息的日期时间,以及 Assistant 消息的用时、首 token、tok/s。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Bobnemimimmi/dsh-always-status-bar", + "added": "2026-08-27" + }, + { + "name": "dsh-focus-overlay", + "owner": "boogoo619", + "url": "https://github.com/boogoo619/dsh-focus-overlay", + "page": "https://awesome-dsh-plugin.com/p/boogoo619/dsh-focus-overlay/", + "category": "ui", + "description": { + "en": "Focus mode with a full-screen reading overlay that hides the header and composer and folds AI tool calls into summaries.", + "zh": "专注模式:全屏阅读视图,隐藏标题与输入区,把 AI 工具调用折叠成摘要。" + }, + "npm": "dsh-focus-overlay", + "stars": 2, + "downloads": 1902, + "install": "dsh plugin --profile web add dsh-focus-overlay", + "added": "2026-08-18" + }, + { + "name": "dsh-conversation-timeline", + "owner": "bycall", + "url": "https://github.com/bycall/dsh-conversation-timeline", + "page": "https://awesome-dsh-plugin.com/p/bycall/dsh-conversation-timeline/", + "category": "ui", + "description": { + "en": "Conversation timeline dock bar for DeepSeek Harness: a turn-level timeline above the composer with user-prompt hover tooltips and click-to-jump back to the exact chat position.", + "zh": "对话时间线横条:输入框上方按轮次聚合的时间线,悬停显示该轮用户提示词,点击直接跳转到对话中的对应位置。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:bycall/dsh-conversation-timeline", + "added": "2026-08-27" + }, + { + "name": "dsh-tick-rail", + "owner": "caisiyang123", + "url": "https://github.com/caisiyang123/dsh-tick-rail", + "page": "https://awesome-dsh-plugin.com/p/caisiyang123/dsh-tick-rail/", + "category": "ui", + "description": { + "en": "Tick-rail conversation navigator: one tick per message you sent, a lit peak that follows the mouse with an even falloff, hover previews, click or keyboard to jump.", + "zh": "会话刻度线导航条:你发的每条消息一格刻度,点亮峰值随鼠标移动、两侧均匀衰减,悬停预览消息摘要,点击或键盘跳转到对应位置。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:caisiyang123/dsh-tick-rail", + "added": "2026-08-18" + }, + { + "name": "dsh-pomodoro", + "owner": "causebefore", + "url": "https://github.com/causebefore/dsh-pomodoro", + "page": "https://awesome-dsh-plugin.com/p/causebefore/dsh-pomodoro/", + "category": "ui", + "description": { + "en": "Pomodoro focus-and-break timer for DSH Web with configurable cycles, a draggable mini panel, and in-app, sound, and browser notifications.", + "zh": "DSH Web 番茄钟:提供可配置专注/休息循环、可拖动迷你面板,以及站内提醒、提示音和浏览器通知。" + }, + "npm": "dsh-pomodoro", + "stars": 3, + "downloads": 1120, + "install": "dsh plugin --profile web add dsh-pomodoro", + "added": "2026-08-14" + }, + { + "name": "dsh-TUI", + "owner": "ccch1mneyyy", + "url": "https://github.com/ccch1mneyyy/dsh-TUI", + "page": "https://awesome-dsh-plugin.com/p/ccch1mneyyy/dsh-TUI/", + "category": "ui", + "description": { + "en": "Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.", + "zh": "Claude Code 风格全屏终端 UI:像素鲸鱼顶栏、实时工作状态行、思考流式展开。" + }, + "npm": "@deepseek-harness-tui/dsh-tui", + "stars": 2553, + "downloads": 34823, + "install": "dsh plugin --profile web add @deepseek-harness-tui/dsh-tui", + "added": "2026-08-14" + }, + { + "name": "dsh-side-panel", + "owner": "ccq1", + "url": "https://github.com/ccq1/dsh-side-panel", + "page": "https://awesome-dsh-plugin.com/p/ccq1/dsh-side-panel/", + "category": "ui", + "description": { + "en": "Side panel with file browser, terminal, and Git review for quick file previews.", + "zh": "侧边栏集成文件浏览器、终端和 Git 审查,方便预览文件。" + }, + "npm": null, + "stars": 16, + "downloads": null, + "install": "dsh plugin --profile web add github:ccq1/dsh-side-panel", + "added": "2026-08-13" + }, + { + "name": "dsh-plugins#dsh-plugin-setting-mcp", + "owner": "Ceelog", + "url": "https://github.com/Ceelog/dsh-plugins/tree/main/src/plugins/dsh-plugin-setting-mcp", + "page": "https://awesome-dsh-plugin.com/p/Ceelog/dsh-plugins--src-plugins-dsh-plugin-setting-mcp/", + "category": "ui", + "description": { + "en": "Manage MCP servers from the Web settings panel: view, add, edit, remove, enable or disable them, with hot reload on save.", + "zh": "在 Web 设置面板中查看、添加、编辑、删除、启用或停用 MCP 服务器,保存后热重载。" + }, + "npm": "@opendsh/dsh-plugin-setting-mcp", + "stars": 8, + "downloads": 1811, + "install": "dsh plugin --profile web add @opendsh/dsh-plugin-setting-mcp", + "added": "2026-08-15" + }, + { + "name": "dsh-turn-fold", + "owner": "CH4ACKO3", + "url": "https://github.com/CH4ACKO3/dsh-turn-fold", + "page": "https://awesome-dsh-plugin.com/p/CH4ACKO3/dsh-turn-fold/", + "category": "ui", + "description": { + "en": "Codex-style turn folding for the DSH Web UI: keeps final answers visible, groups consecutive reasoning and tool activity, and shows configurable live duration, tool-call, and token metrics with multilingual summaries.", + "zh": "面向 DSH Web UI 的 Codex 风格回合折叠:保留最终答复,合并连续的推理与工具活动,并以多语言摘要展示可配置的实时耗时、工具调用和 token 指标。" + }, + "npm": "@ch4acko3/dsh-turn-fold", + "stars": 9, + "downloads": 786, + "install": "dsh plugin --profile web add @ch4acko3/dsh-turn-fold", + "added": "2026-08-21" + }, + { + "name": "dsh-sticky-notes", + "owner": "charrywhite", + "url": "https://github.com/charrywhite/dsh-sticky-notes", + "page": "https://awesome-dsh-plugin.com/p/charrywhite/dsh-sticky-notes/", + "category": "ui", + "description": { + "en": "Draggable sticky notes with 9 skins, image notes, and agent-readable to-do lists for DeepSeek Harness.", + "zh": "可拖动的便签纸,9 款皮肤与图片便签,AI 模型可读取并追加待办条目。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:charrywhite/dsh-sticky-notes", + "added": "2026-08-28" + }, + { + "name": "dsh-file", + "owner": "chengzhi43", + "url": "https://github.com/chengzhi43/dsh-file", + "page": "https://awesome-dsh-plugin.com/p/chengzhi43/dsh-file/", + "category": "ui", + "description": { + "en": "VS Code-style file manager: browse the conversation workspace in a sidebar tree and edit files in the center column with Monaco, with theme import/export and Markdown preview.", + "zh": "VS Code 风格文件管理器:侧边栏文件树浏览当前对话工作区,中间列用 Monaco 编辑文件,支持主题导入导出与 Markdown 预览。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:chengzhi43/dsh-file", + "added": "2026-08-18" + }, + { + "name": "dsh-ui-ux-pro-max", + "owner": "ChenYiming-aaa", + "url": "https://github.com/ChenYiming-aaa/dsh-ui-ux-pro-max", + "page": "https://awesome-dsh-plugin.com/p/ChenYiming-aaa/dsh-ui-ux-pro-max/", + "category": "ui", + "description": { + "en": "Offline-first UI/UX design intelligence library for DeepSeek Harness: design_recommend, design_review and design_search tools with a built-in database of 84 design styles, 192 color palettes, 74 font pairings, 99 UX guidelines, 25 chart types and 22 tech stacks, Chinese-first with zero runtime dependencies.", + "zh": "为 DeepSeek Harness 提供离线可用的 UI/UX 设计智能库:内置 84 种设计风格、192 个配色、74 组字体、99 条 UX 规范、25 种图表与 22 个技术栈,提供 design_recommend / design_review / design_search 三个工具,中文优先、零运行依赖。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:ChenYiming-aaa/dsh-ui-ux-pro-max", + "added": "2026-08-23" + }, + { + "name": "dsh-footer-order", + "owner": "Choi-Peng", + "url": "https://github.com/Choi-Peng/dsh-footer-order", + "page": "https://awesome-dsh-plugin.com/p/Choi-Peng/dsh-footer-order/", + "category": "ui", + "description": { + "en": "DSH sidebar footer ordering: forces the sidebar.footer.action slot into a vertical stack and lets users configure the top-to-bottom order of its entries.", + "zh": "DSH 侧边栏页脚排序:将 sidebar.footer.action 槽位改为上下堆叠,并允许用户配置各条目自上而下的排列顺序。" + }, + "npm": "@choi-p/dsh-footer-order", + "stars": 2, + "downloads": 427, + "install": "dsh plugin --profile web add @choi-p/dsh-footer-order", + "added": "2026-08-19" + }, + { + "name": "dsh-message-outline", + "owner": "chongfengdy-dev", + "url": "https://github.com/chongfengdy-dev/dsh-message-outline", + "page": "https://awesome-dsh-plugin.com/p/chongfengdy-dev/dsh-message-outline/", + "category": "ui", + "description": { + "en": "Current-session message outline rail for the DSH web UI — your messages show as slim bars on the left edge; hover to expand the full outline (line number + text), click to jump instantly to that message. Pure frontend, zero external service dependencies, sessions-snapshot driven with zero polling.", + "zh": "当前会话消息大纲(左缘横杠)——你的消息在对话区左缘显示一列细横杠,hover 展开完整大纲(行号+文本),点击瞬间定位到对话里那条消息。纯前端、零外部服务依赖、sessions 快照驱动零轮询。" + }, + "npm": "dsh-message-outline", + "stars": 2, + "downloads": 359, + "install": "dsh plugin --profile web add dsh-message-outline", + "added": "2026-08-21" + }, + { + "name": "dsh-chat-tidy", + "owner": "ChuanTianML", + "url": "https://github.com/ChuanTianML/dsh-chat-tidy", + "page": "https://awesome-dsh-plugin.com/p/ChuanTianML/dsh-chat-tidy/", + "category": "ui", + "description": { + "en": "One stylesheet that retypesets the DSH conversation to measured Codex metrics: 14/22 px body, tighter heading ladder, list and block rhythm, blockquote bar and table cells, with no settings to choose and before/after captures in the README.", + "zh": "一张样式表,按实测的 Codex 版式重排 DSH 对话页:14/22 px 正文、更紧凑的标题层级、列表与块级节奏、引用条与表格单元格;无需任何设置,README 内附前后对比图。" + }, + "npm": "dsh-chat-tidy", + "stars": 4, + "downloads": 97, + "install": "dsh plugin --profile web add dsh-chat-tidy", + "added": "2026-08-15" + }, + { + "name": "prompt-for-me", + "owner": "ChuanTianML", + "url": "https://github.com/ChuanTianML/prompt-for-me", + "page": "https://awesome-dsh-plugin.com/p/ChuanTianML/prompt-for-me/", + "category": "ui", + "description": { + "en": "Context-aware next-message suggestions in the DSH composer, with repeated Trigger cycling and explicit user review before sending.", + "zh": "DSH 输入框的上下文感知下一句建议:重复 Trigger 可持续换一条,发送前始终由用户确认。" + }, + "npm": "dsh-prompt-for-me", + "stars": 4, + "downloads": 145, + "install": "dsh plugin --profile web add dsh-prompt-for-me", + "added": "2026-08-16" + }, + { + "name": "dsh-deepseek-chat", + "owner": "coldcgh", + "url": "https://github.com/coldcgh/dsh-deepseek-chat", + "page": "https://awesome-dsh-plugin.com/p/coldcgh/dsh-deepseek-chat/", + "category": "ui", + "description": { + "en": "Web chat entry for DeepSeek Harness: a sidebar button opening chat.deepseek.com in a dedicated desktop window under dsh-desktop, with in-webview fallback for plain web environments.", + "zh": "网页对话入口插件:侧边栏一键打开 chat.deepseek.com 独立窗口(dsh-desktop 桥接),纯网页环境自动回退内嵌打开。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:coldcgh/dsh-deepseek-chat", + "added": "2026-08-20" + }, + { + "name": "whale-on-desk", + "owner": "cookiesheep", + "url": "https://github.com/cookiesheep/whale-on-desk", + "page": "https://awesome-dsh-plugin.com/p/cookiesheep/whale-on-desk/", + "category": "ui", + "description": { + "en": "Pixel-art whale desktop companion with 29 frame-audited states — taps the glass and flashes the tab title when an approval waits, speaks a completion report when a turn ends, and adds a slash-command palette to the web client.", + "zh": "像素鲸鱼桌宠,29 个逐帧验收状态:等批准时贴屏敲玻璃并让标签页标题闪烁,任务完成开口汇报,并为 web 端提供斜杠命令面板。" + }, + "npm": "whale-on-desk", + "stars": 6, + "downloads": 7175, + "install": "dsh plugin --profile web add whale-on-desk", + "added": "2026-08-18" + }, + { + "name": "AIsChat#dsh-aischat", + "owner": "Coprexist", + "url": "https://github.com/Coprexist/AIsChat/tree/main/dsh-aischat", + "page": "https://awesome-dsh-plugin.com/p/Coprexist/AIsChat--dsh-aischat/", + "category": "ui", + "description": { + "en": "Embeds the AIsChat social-agent platform into the DSH web UI as a native sidebar board — contact panel, real-time chat over a same-origin HTTP/WS proxy, and Markdown/LaTeX rendering. The plugin is the front end only; it proxies to an AIsChat backend you run yourself (default http://127.0.0.1:5228, from the same repository).", + "zh": "把 AIsChat 社交智能体平台以原生侧边栏形式嵌入 DSH Web 界面——联系人面板、经同源 HTTP/WS 代理的实时聊天、Markdown/LaTeX 渲染。插件只是前端,它代理到你自己运行的 AIsChat 后端(默认 http://127.0.0.1:5228,后端在同一仓库内)。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:Coprexist/AIsChat#path:/dsh-aischat", + "added": "2026-08-20" + }, + { + "name": "dsh-indexbookmark", + "owner": "cuhaitiang0405-collab", + "url": "https://github.com/cuhaitiang0405-collab/dsh-indexbookmark", + "page": "https://awesome-dsh-plugin.com/p/cuhaitiang0405-collab/dsh-indexbookmark/", + "category": "ui", + "description": { + "en": "Conversation question index for the DSH Web UI: every question you asked in a session, searchable, paginated, with one-click jump to the exact message.", + "zh": "DSH 对话问题索引:列出会话中你提过的所有问题,支持搜索、分页与一键定位。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:cuhaitiang0405-collab/dsh-indexbookmark", + "added": "2026-08-19" + }, + { + "name": "dsh-multi-chat", + "owner": "daetz-coder", + "url": "https://github.com/daetz-coder/dsh-multi-chat", + "page": "https://awesome-dsh-plugin.com/p/daetz-coder/dsh-multi-chat/", + "category": "ui", + "description": { + "en": "Multi-window wall for the DSH Web UI: run and monitor N conversations side-by-side in one screen, with auto-discovery, per-window controls, and an authenticated LAN gateway for phone/tablet access.", + "zh": "在 DSH Web 界面里并排运行、监控多个对话实例:多窗口墙 + 自动发现 + 单窗控制,内置带口令认证的局域网网关,手机/平板也能看。" + }, + "npm": "dsh-multi-chat", + "stars": 5, + "downloads": 3754, + "install": "dsh plugin --profile web add dsh-multi-chat", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/daetz-coder/dsh-multi-chat/main/assets/01-windows-dual-chat.png", + "https://raw.githubusercontent.com/daetz-coder/dsh-multi-chat/main/assets/02-ipad-dual-chat.png", + "https://raw.githubusercontent.com/daetz-coder/dsh-multi-chat/main/assets/03-windows-triple-chat.png" + ] + }, + { + "name": "dsh-web-ui#dsh-aionui-panel", + "owner": "DamonKoy", + "url": "https://github.com/DamonKoy/dsh-web-ui/tree/main/packages/dsh-aionui-panel", + "page": "https://awesome-dsh-plugin.com/p/DamonKoy/dsh-web-ui--packages-dsh-aionui-panel/", + "category": "ui", + "description": { + "en": "Right-panel system for the dsh web GUI: a pixel-faithful AionUi Explorer + Preview (file tree, diff view, preview).", + "zh": "dsh web GUI 右侧面板系统:像素级还原 AionUi 的资源树 + 预览(文件树、差异视图、预览)。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:DamonKoy/dsh-web-ui#path:/packages/dsh-aionui-panel", + "added": "2026-08-17" + }, + { + "name": "dsh-web-ui#dsh-liangshen", + "owner": "DamonKoy", + "url": "https://github.com/DamonKoy/dsh-web-ui/tree/main/packages/dsh-liangshen", + "page": "https://awesome-dsh-plugin.com/p/DamonKoy/dsh-web-ui--packages-dsh-liangshen/", + "category": "ui", + "description": { + "en": "LiangShen (梁神) agent preset for the dsh web GUI: a two-phase anchored-standard preset.", + "zh": "dsh web GUI 的梁神 agent 预设:两阶段锚定标准预设。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:DamonKoy/dsh-web-ui#path:/packages/dsh-liangshen", + "added": "2026-08-17" + }, + { + "name": "dsh-web-ui#dsh-task-board", + "owner": "DamonKoy", + "url": "https://github.com/DamonKoy/dsh-web-ui/tree/main/packages/dsh-task-board", + "page": "https://awesome-dsh-plugin.com/p/DamonKoy/dsh-web-ui--packages-dsh-task-board/", + "category": "ui", + "description": { + "en": "Task board for the dsh web GUI: a sidebar entry plus a multi-column kanban view with local persistence.", + "zh": "dsh web GUI 任务看板:侧边栏入口 + 多列看板视图,本地持久化。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:DamonKoy/dsh-web-ui#path:/packages/dsh-task-board", + "added": "2026-08-17" + }, + { + "name": "dsh-web-ui#dsh-web-ui-all", + "owner": "DamonKoy", + "url": "https://github.com/DamonKoy/dsh-web-ui/tree/main/packages/dsh-web-ui-all", + "page": "https://awesome-dsh-plugin.com/p/DamonKoy/dsh-web-ui--packages-dsh-web-ui-all/", + "category": "ui", + "description": { + "en": "DSH Web UI family aggregator: one-click install of all feature plugins (task-board, git-graph, pet, remote-web-ui, live-stats, web-ui-settings).", + "zh": "DSH Web UI 全家桶聚合插件:一键安装全部功能插件(task-board/git-graph/pet/remote-web-ui/live-stats/web-ui-settings)。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:DamonKoy/dsh-web-ui#path:/packages/dsh-web-ui-all", + "added": "2026-08-17" + }, + { + "name": "dsh-web-ui#dsh-web-ui-settings", + "owner": "DamonKoy", + "url": "https://github.com/DamonKoy/dsh-web-ui/tree/main/packages/dsh-web-ui-settings", + "page": "https://awesome-dsh-plugin.com/p/DamonKoy/dsh-web-ui--packages-dsh-web-ui-settings/", + "category": "ui", + "description": { + "en": "Web UI plugin group card in the dsh settings page: enable switches and config for the dsh-web-ui family.", + "zh": "dsh 设置页的 Web UI 插件组卡片:dsh-web-ui 全家插件的开关与配置。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:DamonKoy/dsh-web-ui#path:/packages/dsh-web-ui-settings", + "added": "2026-08-17" + }, + { + "name": "dsh-file-drop", + "owner": "dannyvan", + "url": "https://github.com/dannyvan/dsh-file-drop", + "page": "https://awesome-dsh-plugin.com/p/dannyvan/dsh-file-drop/", + "category": "ui", + "description": { + "en": "Drag & drop or click to attach any non-image file (PDF/Word/Excel/ZIP/text) into the composer: in the desktop shell it resolves the real Finder path instantly; otherwise it uploads to the workspace and inserts the path.", + "zh": "拖拽/点击上传任意非图片文件(PDF/Word/Excel/ZIP/文本)到输入框:桌面壳下直取 Finder 原始路径;无壳时上传到工作区并插入路径。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:dannyvan/dsh-file-drop", + "added": "2026-08-16" + }, + { + "name": "dsh-enhance-tool", + "owner": "dcrzsy", + "url": "https://github.com/dcrzsy/dsh-enhance-tool", + "page": "https://awesome-dsh-plugin.com/p/dcrzsy/dsh-enhance-tool/", + "category": "ui", + "description": { + "en": "All-in-one UI enhancement layer for DeepSeek Harness web: prompt library with quick-insert and management, two-step AI polish, predicted-reply dock with click-to-send, MCP tool management, scheduled automation, smart session titles, adjustable chat width, full-width AI messages without gray background, auto-collapse for long user messages with smooth expand/collapse animations, and scene-specific entrance animations across all plugin surfaces.", + "zh": "DeepSeek Harness Web 一站式界面增强:提示词库(快选+管理)、两步 AI 润色、预测回复栏(点击直接发送)、MCP 工具管理、定时自动化、智能会话标题、对话宽度可调、AI 回复占满无灰底、超长用户消息自动折叠(带平滑展开/收起动效),以及各面板的场景化入场动效。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:dcrzsy/dsh-enhance-tool", + "added": "2026-08-24" + }, + { + "name": "dsh-workspace-dir", + "owner": "DDSG-X", + "url": "https://github.com/DDSG-X/dsh-workspace-dir", + "page": "https://awesome-dsh-plugin.com/p/DDSG-X/dsh-workspace-dir/", + "category": "ui", + "description": { + "en": "Shows the current conversation's working directory in a draggable directory panel with adjustable opacity.", + "zh": "在可拖动、透明度可调的目录面板中显示当前对话的工作目录与文件列表。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:DDSG-X/dsh-workspace-dir", + "added": "2026-08-15" + }, + { + "name": "dsh-jumpbar", + "owner": "delaydai", + "url": "https://github.com/delaydai/dsh-jumpbar", + "page": "https://awesome-dsh-plugin.com/p/delaydai/dsh-jumpbar/", + "category": "ui", + "description": { + "en": "User message jump bar for the DSH web GUI: a minimap-style dash strip on the right edge of the conversation, with hover preview, click-to-jump, drag-to-scrub and a viewport marker.", + "zh": "DSH Web 用户消息跳转条:对话区右缘 minimap 式横杠条带,悬停预览消息内容、点击跳转置顶、拖拽快速浏览,并带当前视口标记。" + }, + "npm": null, + "stars": 0, + "downloads": null, + "install": "dsh plugin --profile web add github:delaydai/dsh-jumpbar", + "added": "2026-08-25" + }, + { + "name": "dsh-airdrop", + "owner": "demacia1314", + "url": "https://github.com/demacia1314/dsh-airdrop", + "page": "https://awesome-dsh-plugin.com/p/demacia1314/dsh-airdrop/", + "category": "ui", + "description": { + "en": "⭐ Drag-and-drop uploads of any file or whole folders, anywhere in the window; remote-safe — bytes go straight into the session workspace on the server, so a DSH deployed on a remote machine works exactly like localhost.", + "zh": "⭐ 支持拖拽上传:任意文件、整个文件夹,拖到窗口任意位置即可;支持远程上传——字节直传服务器的会话工作区,部署在服务器上的 DSH 也能像本地一样拖了就发。" + }, + "npm": "dsh-universal-attachments", + "stars": 3, + "downloads": 418, + "install": "dsh plugin --profile web add dsh-universal-attachments", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/demacia1314/dsh-airdrop/main/assets/drop-in.png", + "https://raw.githubusercontent.com/demacia1314/dsh-airdrop/main/assets/in-chat.png", + "https://raw.githubusercontent.com/demacia1314/dsh-airdrop/main/assets/preview-modal.png" + ] + }, + { + "name": "deepseek-design#deepseek-idesign", + "owner": "Devin-AXIS", + "url": "https://github.com/Devin-AXIS/deepseek-design/tree/main/packages/deepseek-idesign", + "page": "https://awesome-dsh-plugin.com/p/Devin-AXIS/deepseek-design--packages-deepseek-idesign/", + "category": "ui", + "description": { + "en": "Visual design studio for websites, app prototypes, posters, cards, reports, and magazines, with templates, direct element editing, selection-aware AI draft handoff, and export.", + "zh": "可视化设计工作室,支持网站、App 原型、海报、信息卡、报告和杂志的模板创建、元素编辑、选区级 AI 草稿衔接与导出。" + }, + "npm": null, + "stars": 467, + "downloads": null, + "install": "dsh plugin --profile web add github:Devin-AXIS/deepseek-design#path:/packages/deepseek-idesign", + "added": "2026-08-16" + }, + { + "name": "deepseek-design#deepseek-ivideo", + "owner": "Devin-AXIS", + "url": "https://github.com/Devin-AXIS/deepseek-design/tree/main/packages/deepseek-ivideo", + "page": "https://awesome-dsh-plugin.com/p/Devin-AXIS/deepseek-design--packages-deepseek-ivideo/", + "category": "ui", + "description": { + "en": "Visual video studio with an editable timeline, animation and media controls, 27 templates, whole-video and selection-aware AI draft handoff, validation, preview, and export.", + "zh": "可视化视频工作室,支持可编辑时间线、动画和素材控制、27 个模板、整段与选区级 AI 草稿衔接、校验、预览和导出。" + }, + "npm": null, + "stars": 467, + "downloads": null, + "install": "dsh plugin --profile web add github:Devin-AXIS/deepseek-design#path:/packages/deepseek-ivideo", + "added": "2026-08-19" + }, + { + "name": "pbr-render", + "owner": "dhb861832993-star", + "url": "https://github.com/dhb861832993-star/pbr-render", + "page": "https://awesome-dsh-plugin.com/p/dhb861832993-star/pbr-render/", + "category": "ui", + "description": { + "en": "PBR 3D model preview for game art: GLB/GLTF with textures, IBL environment lighting, orbit controls, and a material channel inspector (baseColor/normal/roughness/metallic/AO/emissive/wireframe); scalar channels shown as grayscale per PBR spec, via the pbr3d fence and pbr_render tool.", + "zh": "游戏美术 PBR 3D 模型预览:GLB/GLTF 带贴图纹理、IBL 环境光照、轨道控制,以及材质通道检查器(基础色/法线/粗糙度/金属度/AO/自发光/线框),标量通道按 PBR 规范以灰度显示,通过 pbr3d 围栏与 pbr_render 工具使用。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:dhb861832993-star/pbr-render", + "added": "2026-08-16" + }, + { + "name": "dsh-better-input", + "owner": "DIAG5", + "url": "https://github.com/DIAG5/dsh-better-input", + "page": "https://awesome-dsh-plugin.com/p/DIAG5/dsh-better-input/", + "category": "ui", + "description": { + "en": "Input experience enhancement: voice input, AI polish, anti-overwrite, prompt optimization with diff preview, and auto locale switching following DSH UI language.", + "zh": "输入体验增强插件,目前已实现语音输入、AI提示词润色(反覆盖保护、带差异预览的提示词优化)、跟随 DSH 界面语言自动切换中英文界面。" + }, + "npm": "dsh-better-input", + "stars": 2, + "downloads": 986, + "install": "dsh plugin --profile web add dsh-better-input", + "added": "2026-08-23" + }, + { + "name": "dsh-focus-chat", + "owner": "dingyi222666", + "url": "https://github.com/dingyi222666/dsh-focus-chat", + "page": "https://awesome-dsh-plugin.com/p/dingyi222666/dsh-focus-chat/", + "category": "ui", + "description": { + "en": "A \"focus chat\" minimal view that shows only final outputs.", + "zh": "「聚焦会话」精简视图,只关注最终产出结果。" + }, + "npm": null, + "stars": 22, + "downloads": null, + "install": "dsh plugin --profile web add github:dingyi222666/dsh-focus-chat", + "added": "2026-08-13" + }, + { + "name": "dsh-conversation-rail", + "owner": "dingyi580", + "url": "https://github.com/dingyi580/dsh-conversation-rail", + "page": "https://awesome-dsh-plugin.com/p/dingyi580/dsh-conversation-rail/", + "category": "ui", + "description": { + "en": "Left-edge conversation rail for the Web UI: one bar per turn, length scaled to the turn text, hover for a question and answer preview, click to jump; bars come from a host-side outline of the whole session log, so turns outside the loaded window are drawn too and history is paged in on click.", + "zh": "对话左缘的导航轨:一根杠一轮对话,杠长按该轮文字体量,悬停出提问与回答预览,点击跳转;杠来自 host 侧读整份会话日志折出的大纲,未加载进窗口的轮次也画得出来,点击时再翻页补历史。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:dingyi580/dsh-conversation-rail", + "added": "2026-08-18" + }, + { + "name": "OLEDCare", + "owner": "domparent", + "url": "https://github.com/domparent/OLEDCare", + "page": "https://awesome-dsh-plugin.com/p/domparent/OLEDCare/", + "category": "ui", + "description": { + "en": "OLED burn-in care for the Web GUI with pure-black surfaces, gamma-aware text dimming on an idle/focus ladder, a true-black nap screensaver with live agent status, and slow accent-hue rotation.", + "zh": "OLED burn-in care for the Web GUI with pure-black surfaces, gamma-aware text dimming on an idle/focus ladder, a true-black nap screensaver with live agent status, and slow accent-hue rotation." + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:domparent/OLEDCare", + "added": "2026-08-23" + }, + { + "name": "dsh-input-traffic", + "owner": "drscrewdriver", + "url": "https://github.com/drscrewdriver/dsh-input-traffic", + "page": "https://awesome-dsh-plugin.com/p/drscrewdriver/dsh-input-traffic/", + "category": "ui", + "description": { + "en": "Three-tier now/next/later input planning dock for the DeepSeek Harness web GUI: a red/yellow/green priority strip replaces the busy-Enter queue, plain Enter queues to later, with freeze/resume and in-place multi-line editing.", + "zh": "三档(now/next/later)输入规划 dock:以红/黄/绿色优先级条取代忙碌 Enter 排队栏,普通 Enter 排入 later,支持冻结/恢复与多行原位编辑。" + }, + "npm": "dsh-input-traffic", + "stars": 4, + "downloads": 1815, + "install": "dsh plugin --profile web add dsh-input-traffic", + "added": "2026-08-18" + }, + { + "name": "dsh-niao-quick-open", + "owner": "dsh-niao", + "url": "https://github.com/dsh-niao/dsh-niao-quick-open", + "page": "https://awesome-dsh-plugin.com/p/dsh-niao/dsh-niao-quick-open/", + "category": "ui", + "description": { + "en": "UI enhancement suite for DeepSeek Harness: one-click workspace open (copy path, reveal in Finder, open in an auto-discovered editor), DeepSeek-web-style user-message nav rail, session to-do marks, flat-list polish, and a hard-restart button.", + "zh": "DeepSeek Harness 界面增强插件:工作区一键打开(复制路径、访达显示、自动发现编辑器打开)、用户消息导航条、会话待办标记、单列表增强与硬性重启按钮。" + }, + "npm": "dsh-niao-quick-open", + "stars": 4, + "downloads": 1365, + "install": "dsh plugin --profile web add dsh-niao-quick-open", + "added": "2026-08-19" + }, + { + "name": "dsh-recover-context", + "owner": "dygin", + "url": "https://github.com/dygin/dsh-recover-context", + "page": "https://awesome-dsh-plugin.com/p/dygin/dsh-recover-context/", + "category": "ui", + "description": { + "en": "Roll back to before any of your questions or edit the last one and resubmit, with the workspace files of the discarded turns restored.", + "zh": "回退到任意一条提问之前,或编辑上一条问题重新提交;被丢弃轮次产生的工作区文件会一并还原。" + }, + "npm": "dsh-recover-context", + "stars": 1, + "downloads": 936, + "install": "dsh plugin --profile web add dsh-recover-context", + "added": "2026-08-23" + }, + { + "name": "dsh-popout-sidebar", + "owner": "e2mcc", + "url": "https://github.com/e2mcc/dsh-popout-sidebar", + "page": "https://awesome-dsh-plugin.com/p/e2mcc/dsh-popout-sidebar/", + "category": "ui", + "description": { + "en": "Pop-out sidebar: lists artifacts and a file tree with multiple preview formats, pops out into a separate browser tab you can drag to another monitor, and coexists with other sidebar plugins simultaneously.", + "zh": "可弹出式侧边栏:侧边栏展示产物与文件树,支持多种文件预览形式;并可弹出为独立浏览器标签页(可拖至另一显示器);兼容其他sidebar插件,可以同时显示。" + }, + "npm": "dsh-popout-sidebar", + "stars": 100, + "downloads": 458, + "install": "dsh plugin --profile web add dsh-popout-sidebar", + "added": "2026-08-17" + }, + { + "name": "packhub-workbench-assistant", + "owner": "eomis", + "url": "https://github.com/eomis/packhub-workbench-assistant", + "page": "https://awesome-dsh-plugin.com/p/eomis/packhub-workbench-assistant/", + "category": "ui", + "description": { + "en": "Adds quick installation and switching for workbench templates in DSH Desktop.", + "zh": "为 DSH Desktop 提供工作台模板的快捷安装与切换。" + }, + "npm": null, + "tarball": "https://github.com/eomis/packhub-workbench-assistant/releases/latest/download/packhub-workbench-assistant.tgz", + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/eomis/packhub-workbench-assistant/releases/latest/download/packhub-workbench-assistant.tgz\"", + "added": "2026-08-24" + }, + { + "name": "dsh-theme", + "owner": "EternalNight996", + "url": "https://github.com/EternalNight996/dsh-theme", + "page": "https://awesome-dsh-plugin.com/p/EternalNight996/dsh-theme/", + "category": "ui", + "description": { + "en": "Theme skin plugin for DeepSeek Harness — built-in themes / static image / dynamic 360°-follow video, one-click from the sidebar & Settings; imports persist, default skins protected.", + "zh": "DeepSeek Harness 主题皮肤插件:内置主题 / 静态图片 / 动态视频(鼠标环绕跟随帧),侧边栏一键切换,设置页完整管理;导入持久化、默认皮肤不可删。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:EternalNight996/dsh-theme", + "added": "2026-08-27" + }, + { + "name": "dsh-ui-agents-pixe", + "owner": "EternalNight996", + "url": "https://github.com/EternalNight996/dsh-ui-agents-pixe", + "page": "https://awesome-dsh-plugin.com/p/EternalNight996/dsh-ui-agents-pixe/", + "category": "ui", + "description": { + "en": "Adds a \"Work Roles\" tab and a pixel-art office overlay to the DeepSeek Harness Web UI: 508 built-in role cards (The Agency 255 + agency-agents-zh 253) with search, EN/ZH toggle and department filter; draggable Canvas 2D pixel characters that stand, type and pace, with idle chat that can plug into AI. One-command npm install, no dsh source changes.", + "zh": "为 DeepSeek Harness Web 主窗口添加「工作角色」页签 + 像素办公室浮层:内置 508 张角色卡(The Agency 255 + agency-agents-zh 253),支持搜索 / 中英切换 / 分部分类;Canvas 2D 像素小人可站立、打字、踱步,闲聊台词可接 AI。一条命令安装,不改 dsh 源码。" + }, + "npm": "dsh-ui-agents-pixe", + "stars": 3, + "downloads": 2407, + "install": "dsh plugin --profile web add dsh-ui-agents-pixe", + "added": "2026-08-18" + }, + { + "name": "dsh-ui-three-body", + "owner": "EternalNight996", + "url": "https://github.com/EternalNight996/dsh-ui-three-body", + "page": "https://awesome-dsh-plugin.com/p/EternalNight996/dsh-ui-three-body/", + "category": "ui", + "description": { + "en": "Adds a sentient floating eye to DeepSeek Harness — injects a taming kernel that makes the AI truly understand you, with 11 dynamic eye skins, ghost mode, and zero token cost when AI mode is off.", + "zh": "给 DeepSeek Harness 装上会开智的智子:注入驯兽内核让 AI 真正懂你,11 款动态皮肤、幽灵模式,关闭 AI 模式即零 token 消耗。" + }, + "npm": "dsh-ui-three-body", + "stars": 2, + "downloads": 339, + "install": "dsh plugin --profile web add dsh-ui-three-body", + "added": "2026-08-19" + }, + { + "name": "dsh_session_folders", + "owner": "EugeneVl", + "url": "https://github.com/EugeneVl/dsh_session_folders", + "page": "https://awesome-dsh-plugin.com/p/EugeneVl/dsh_session_folders/", + "category": "ui", + "description": { + "en": "One-level session folders for the web sidebar - group sessions per workspace, move them by drag-and-drop or context menu, archive and restore, server-side persistence.", + "zh": "为网页侧边栏提供单层会话文件夹——按工作区分组会话,支持拖拽或右键菜单移动、归档与恢复,数据在服务端持久化。" + }, + "npm": "dsh-session-folders", + "stars": 4, + "downloads": 425, + "install": "dsh plugin --profile web add dsh-session-folders", + "added": "2026-08-25", + "screenshots": [ + "https://raw.githubusercontent.com/EugeneVl/dsh_session_folders/master/assets/screenshot-1.jpg", + "https://raw.githubusercontent.com/EugeneVl/dsh_session_folders/master/assets/screenshot-2.jpg", + "https://raw.githubusercontent.com/EugeneVl/dsh_session_folders/master/assets/screenshot-3.jpg" + ] + }, + { + "name": "dsh-tui-pi", + "owner": "fan56", + "url": "https://github.com/fan56/dsh-tui-pi", + "page": "https://awesome-dsh-plugin.com/p/fan56/dsh-tui-pi/", + "category": "ui", + "description": { + "en": "Full-featured TUI for DeepSeek Harness: streaming conversation, powerline status bar, real-time think/tool/todo panels, subagent dual-view, theme hot-switch — all as pure cordis plugins with zero upstream patches.", + "zh": "DeepSeek Harness 的全功能 TUI:流式对话、powerline 状态栏、实时思考/工具/待办面板、子代理双视图、主题热切换——全部以纯 cordis 插件实现,不 fork、不 patch 上游一行代码。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:fan56/dsh-tui-pi", + "added": "2026-08-28" + }, + { + "name": "dsh-office", + "owner": "Fayelin12", + "url": "https://github.com/Fayelin12/dsh-office", + "page": "https://awesome-dsh-plugin.com/p/Fayelin12/dsh-office/", + "category": "ui", + "description": { + "en": "Office workspace & session dashboard: a floating 6-column sprite panel visualizing workspaces, sessions, token usage and subagents, plus Agent Mail and an office log tab. Feishu/Lark views (message feed, meeting schedules, transcripts) additionally require the `@larksuite/cli` tool installed and authenticated separately.", + "zh": "办公室工作区/会话仪表盘:悬浮 6 列精灵面板,可视化工作区、会话、token 用量与子代理,并提供 Agent Mail 与办公日志页签。飞书相关视图(消息流、会议日程、妙记逐字稿)另需自行安装并授权 `@larksuite/cli`。" + }, + "npm": "dsh-office", + "stars": 4, + "downloads": 2089, + "install": "dsh plugin --profile web add dsh-office", + "added": "2026-08-15" + }, + { + "name": "dsh-prompt", + "owner": "FeatherHunter", + "url": "https://github.com/FeatherHunter/dsh-prompt", + "page": "https://awesome-dsh-plugin.com/p/FeatherHunter/dsh-prompt/", + "category": "ui", + "description": { + "en": "Right-side prompt toolbox panel with 24 built-in deep templates plus user-defined ones, accessible via the /prompt slash trigger and a context-aware floating suggestion card; one-click inserts the selected prompt into the composer.", + "zh": "输入框旁的 prompt 工具箱面板:24 条内置深度模板 + 自定义模板,/prompt 触发源 + 智能推荐悬浮卡,点选即插入当前对话。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:FeatherHunter/dsh-prompt", + "added": "2026-08-18" + }, + { + "name": "dsh-stock-ticker", + "owner": "FeiZhuNiU-INFJA", + "url": "https://github.com/FeiZhuNiU-INFJA/dsh-stock-ticker", + "page": "https://awesome-dsh-plugin.com/p/FeiZhuNiU-INFJA/dsh-stock-ticker/", + "category": "ui", + "description": { + "en": "A floating, semi-transparent market ticker for DeepSeek Harness showing real-time level and change percentage for the Shanghai Composite, ChiNext, STAR 50 and Hang Seng Tech indices.", + "zh": "一个悬浮的半透明行情小窗,实时显示上证指数、创业板指、科创50 与恒生科技的当前点位与涨跌幅。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:FeiZhuNiU-INFJA/dsh-stock-ticker", + "added": "2026-08-22", + "screenshots": [ + "https://raw.githubusercontent.com/FeiZhuNiU-INFJA/dsh-stock-ticker/main/assets/screenshot.png" + ] + }, + { + "name": "dsh-session-icons", + "owner": "fengb3", + "url": "https://github.com/fengb3/dsh-session-icons", + "page": "https://awesome-dsh-plugin.com/p/fengb3/dsh-session-icons/", + "category": "ui", + "description": { + "en": "Sidebar session-title icons: an auxiliary model call on the same route as title generation draws a 24x24 monochrome SVG metaphor shown before each session row title, with disk caching.", + "zh": "侧边栏会话标题图标:标题生成时用同一路由的辅助模型调用画一枚 24×24 单色 SVG 隐喻图标,显示在每行标题左侧,带磁盘缓存。" + }, + "npm": "dsh-session-icons", + "stars": 2, + "downloads": 230, + "install": "dsh plugin --profile web add dsh-session-icons", + "added": "2026-08-24", + "screenshots": [ + "https://raw.githubusercontent.com/fengb3/dsh-session-icons/master/docs/screenshot.png" + ] + }, + { + "name": "dsh-line-select", + "owner": "fengMax1997", + "url": "https://github.com/fengMax1997/dsh-line-select", + "page": "https://awesome-dsh-plugin.com/p/fengMax1997/dsh-line-select/", + "category": "ui", + "description": { + "en": "Line-range selection: browse workspace files, preview code with line numbers, visually select a line range, and write a `@path:start-end` reference into the composer; the agent receives the exact selected lines on send.", + "zh": "行选区:浏览工作区文件、带行号预览代码、可视化选中行范围,一键把 `@path:start-end` 引用写入输入框,agent 发送时自动收到所选行原文,精准修改指定行。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:fengMax1997/dsh-line-select", + "added": "2026-08-15" + }, + { + "name": "dsh-sticky-notes", + "owner": "flyhigao", + "url": "https://github.com/flyhigao/dsh-sticky-notes", + "page": "https://awesome-dsh-plugin.com/p/flyhigao/dsh-sticky-notes/", + "category": "ui", + "description": { + "en": "Workspace sticky notes for DSH, saved as multiple Markdown files under the current workspace.", + "zh": "DSH 工作区极简便签:在各个工作区保存多张 Markdown 便签,记录你的想法和项目进程。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:flyhigao/dsh-sticky-notes", + "added": "2026-08-18" + }, + { + "name": "dsh-message-nav", + "owner": "foooold", + "url": "https://github.com/foooold/dsh-message-nav", + "page": "https://awesome-dsh-plugin.com/p/foooold/dsh-message-nav/", + "category": "ui", + "description": { + "en": "Adds a Codex-style message navigation rail for quickly locating earlier prompts in long DeepSeek Harness Web UI conversations.", + "zh": "为 DeepSeek Harness Web UI 添加 Codex 风格的消息导航轨道,方便在长会话中快速定位历史提问。" + }, + "npm": "dsh-message-nav", + "stars": 1, + "downloads": 243, + "install": "dsh plugin --profile web add dsh-message-nav", + "added": "2026-08-24", + "screenshots": [ + "https://raw.githubusercontent.com/foooold/dsh-message-nav/HEAD/assets/rail-left-edge.png", + "https://raw.githubusercontent.com/foooold/dsh-message-nav/HEAD/assets/rail-hover-preview.png" + ] + }, + { + "name": "dsh-chat-tools", + "owner": "Free-corner", + "url": "https://github.com/Free-corner/dsh-chat-tools", + "page": "https://awesome-dsh-plugin.com/p/Free-corner/dsh-chat-tools/", + "category": "ui", + "description": { + "en": "Chat column width and alignment control, numbered question history with keyboard navigation and auto-load of older questions, and markdown outline rails for both the conversation and the file preview.", + "zh": "对话列宽度与位置调节、带序号与快捷键跳转的提问历史(支持自动加载更早提问),以及对话区和文件预览区的 Markdown 大纲目录。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:Free-corner/dsh-chat-tools", + "added": "2026-08-22" + }, + { + "name": "dsh-prompt-vault", + "owner": "Frog755", + "url": "https://github.com/Frog755/dsh-prompt-vault", + "page": "https://awesome-dsh-plugin.com/p/Frog755/dsh-prompt-vault/", + "category": "ui", + "description": { + "en": "Prompt library above the DSH composer — a 📚 button expands a searchable panel to insert saved prompts into the draft with one click (appends, never overwrites), persisted in a host-side JSON file.", + "zh": "输入框上方的提示词库:📚 按钮展开可搜索面板,点条目一键填入草稿(追加不覆盖);数据以 JSON 文件保存在 Host 侧。" + }, + "npm": "@frog755/dsh-prompt-vault", + "stars": 1, + "downloads": 236, + "install": "dsh plugin --profile web add @frog755/dsh-prompt-vault", + "added": "2026-08-24", + "screenshots": [ + "https://raw.githubusercontent.com/Frog755/dsh-prompt-vault/HEAD/assets/screenshot-panel.png" + ] + }, + { + "name": "dsh-wallpaper", + "owner": "Frog755", + "url": "https://github.com/Frog755/dsh-wallpaper", + "page": "https://awesome-dsh-plugin.com/p/Frog755/dsh-wallpaper/", + "category": "ui", + "description": { + "en": "Persistent image or MP4 wallpaper for the DSH Web profile: a wallpaper card under Settings -> General selects a local image or MP4 (up to 300 MB), tunes surface opacity and blur, compresses video locally to H.264 when ffmpeg/ffprobe are on PATH, stores media under ~/.dsh/wallpapers, and pins the Web server to port 9191 so the saved wallpaper survives restarts.", + "zh": "DSH Web 持久化背景图/视频插件:在 设置 -> 通用 中提供壁纸卡片,可选本地图片或 MP4(最大 300 MB),调节表层透明度与模糊;检测到 ffmpeg/ffprobe 时在本机把视频压缩为 H.264;媒体保存到 ~/.dsh/wallpapers;并将 Web 服务固定到 9191 端口以便重启后恢复壁纸。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Frog755/dsh-wallpaper", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/Frog755/dsh-wallpaper/HEAD/assets/wallpaper-effect.png", + "https://raw.githubusercontent.com/Frog755/dsh-wallpaper/HEAD/assets/wallpaper-settings.png" + ] + }, + { + "name": "dsh-desktop#packages/dsh-desktop-plugin", + "owner": "FuqiangCraft", + "url": "https://github.com/FuqiangCraft/dsh-desktop/tree/main/packages/dsh-desktop-plugin", + "page": "https://awesome-dsh-plugin.com/p/FuqiangCraft/dsh-desktop--packages-dsh-desktop-plugin/", + "category": "ui", + "description": { + "en": "Desktop-grade companion: browser native notifications for user questions and approval interactions, screen capture model tool, multi-agent tiling grid view, and lightweight Tauri desktop shell.", + "zh": "桌面级增强套件:原生提问/审批交互通知、屏幕截图工具、多智能体平铺网格视图与轻量级 Tauri 桌面外壳。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:FuqiangCraft/dsh-desktop#path:/packages/dsh-desktop-plugin", + "added": "2026-08-26" + }, + { + "name": "dsh-peak-indicator", + "owner": "future007s", + "url": "https://github.com/future007s/dsh-peak-indicator", + "page": "https://awesome-dsh-plugin.com/p/future007s/dsh-peak-indicator/", + "category": "ui", + "description": { + "en": "Session header badge showing DeepSeek peak/off-peak status, per-model token prices on hover, and real session/turn costs from actual usage.", + "zh": "会话头部徽标:显示 DeepSeek 高峰/闲时状态、当前模型 token 单价,并按真实用量统计会话与每轮费用。" + }, + "npm": "dsh-peak-indicator", + "stars": 5, + "downloads": 2690, + "install": "dsh plugin --profile web add dsh-peak-indicator", + "added": "2026-08-18" + }, + { + "name": "dsh-sidebar-width", + "owner": "garrisonz", + "url": "https://github.com/garrisonz/dsh-sidebar-width", + "page": "https://awesome-dsh-plugin.com/p/garrisonz/dsh-sidebar-width/", + "category": "ui", + "description": { + "en": "Control the Web UI left sidebar width: lower the 264px drag minimum and optionally tune the drag maximum and the expanded default width, applied by a startup patch of the ui-layout bundle.", + "zh": "调整 Web UI 左侧会话列表栏宽度:调低 264px 拖动下限,可选调整拖动上限与展开默认宽度,启动时自动修补 ui-layout bundle。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:garrisonz/dsh-sidebar-width", + "added": "2026-08-15" + }, + { + "name": "dsh-balance", + "owner": "GeekRicardo", + "url": "https://github.com/GeekRicardo/dsh-balance", + "page": "https://awesome-dsh-plugin.com/p/GeekRicardo/dsh-balance/", + "category": "ui", + "description": { + "en": "Composer-dock balance/usage readout for DeepSeek, Kimi Coding, OpenCode Go, Zhipu, MiniMax, OpenRouter and OpenAI Codex — provider-based, 2s polling, 5min cache.", + "zh": "输入框下方按 provider 展示 DeepSeek、Kimi Coding、OpenCode Go、智谱、MiniMax、OpenRouter、OpenAI Codex 的余额/用量,2 秒轮询、5 分钟缓存。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:GeekRicardo/dsh-balance", + "added": "2026-08-18" + }, + { + "name": "dsh-convmap", + "owner": "GeekRicardo", + "url": "https://github.com/GeekRicardo/dsh-convmap", + "page": "https://awesome-dsh-plugin.com/p/GeekRicardo/dsh-convmap/", + "category": "ui", + "description": { + "en": "Conversation-map rail down the left edge of the transcript: one tick per user turn, read from the full session log on the host rather than from the rendered page, so long sessions are covered end to end. Hovering fans the neighbouring ticks open by pixel distance and floats a scrollable preview of that turn prompt and its last reply; clicking scrolls to the turn, paging older history in first when it has not been rendered yet; the tick for the visible turn stays highlighted while you scroll.", + "zh": "主对话区左缘的对话地图刻度列:每条刻度是一轮用户提问,刻度由宿主直读会话完整日志得来,不受客户端分页窗口限制,长会话也是全量覆盖。hover 时邻近刻度按像素距离连续展开,右侧浮出该轮提问与最后一次回复的预览卡(可进卡滚动看完);点击跳转到该轮,老轮次尚未渲染时先自动分页加载再跳;滚动对话时当前轮次的刻度保持高亮。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:GeekRicardo/dsh-convmap", + "added": "2026-08-23" + }, + { + "name": "dsh-office-plugin", + "owner": "geguanming", + "url": "https://github.com/geguanming/dsh-office-plugin", + "page": "https://awesome-dsh-plugin.com/p/geguanming/dsh-office-plugin/", + "category": "ui", + "description": { + "en": "Renders live multi-agent session activity as a pixel-art office in a Web UI side panel - each session is an ox/horse worker typing at its desk when busy or wandering off when idle, with click-to-view work monitors and in-place approval cards.", + "zh": "把多 agent 会话的实时活动渲染成 Web UI 侧边栏里的像素风办公室--每个会话是一名牛/马员工,忙时伏案敲键盘、闲时起身摸鱼,支持点击查看工位监控、就地处理审批。" + }, + "npm": "dsh-office-plugin", + "tarball": "https://github.com/geguanming/dsh-office-plugin/releases/latest/download/dsh-office-plugin.tgz", + "stars": 2, + "downloads": 553, + "install": "dsh plugin --profile web add dsh-office-plugin", + "added": "2026-08-20", + "screenshots": [ + "https://raw.githubusercontent.com/geguanming/dsh-office-plugin/master/assets/entry.png", + "https://raw.githubusercontent.com/geguanming/dsh-office-plugin/master/assets/work-monitor.png", + "https://raw.githubusercontent.com/geguanming/dsh-office-plugin/master/assets/order-boss.png" + ] + }, + { + "name": "dsh-model-picker", + "owner": "genius-alray", + "url": "https://github.com/genius-alray/dsh-model-picker", + "page": "https://awesome-dsh-plugin.com/p/genius-alray/dsh-model-picker/", + "category": "ui", + "description": { + "en": "Dialog-style model picker for the DSH Web UI: fuzzy search, collapsible provider groups, favorites with a favorites-only filter, and a thinking-level dropdown, replacing the built-in model selector with official UI primitives and DSW tokens.", + "zh": "Web UI 模型选择器替换插件:对话框式选择,支持模糊搜索、供应商折叠分组、收藏与只看收藏过滤,以及思考档位下拉,复用官方组件库与 DSW 设计 token。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:genius-alray/dsh-model-picker", + "added": "2026-08-16" + }, + { + "name": "terminal", + "owner": "giiiiiithub", + "url": "https://github.com/giiiiiithub/terminal", + "page": "https://awesome-dsh-plugin.com/p/giiiiiithub/terminal/", + "category": "ui", + "description": { + "en": "Real PTY terminal panel for the DSH Web UI: host half spawns shells with node-pty (cmd.exe default on Windows), rendered in the browser with xterm.js; multi-tab sessions, dock/floating window, clipboard support.", + "zh": "DSH Web UI 终端面板:宿主端 node-pty 真实 PTY(Windows 默认 cmd.exe)+ 浏览器 xterm.js 渲染;多标签会话、停靠/浮动窗口、复制粘贴。" + }, + "npm": "dsh-terminal", + "stars": 1, + "downloads": 550, + "install": "dsh plugin --profile web add dsh-terminal", + "added": "2026-08-15" + }, + { + "name": "dsh-conversation-navigator", + "owner": "gjj-star", + "url": "https://github.com/gjj-star/dsh-conversation-navigator", + "page": "https://awesome-dsh-plugin.com/p/gjj-star/dsh-conversation-navigator/", + "category": "ui", + "description": { + "en": "Conversation navigator panel: turn-folded outline of the current session with per-step kind badges (assistant, tool, command, compaction, error), a hover bubble showing the full user question, a minimal mode that hides turn numbers for an all-badge view, keyword filter over user questions and assistant replies, click-to-jump with reading-position tracking, and load-earlier/load-all history paging.", + "zh": "会话导航面板:按轮折叠的当前会话大纲与步次类型徽标(助手/工具/命令/压缩/出错),悬停气泡显示用户提问全文,可隐藏轮次号的极简徽标视图,仅检索提问与助手回复的关键词过滤,点击跳转与阅读位置跟踪,支持加载更早/加载全部历史分页。" + }, + "npm": "dsh-conversation-navigator", + "stars": 7, + "downloads": 3530, + "install": "dsh plugin --profile web add dsh-conversation-navigator", + "added": "2026-08-22", + "screenshots": [ + "https://raw.githubusercontent.com/gjj-star/dsh-conversation-navigator/HEAD/assets/screenshots/01-main.png", + "https://raw.githubusercontent.com/gjj-star/dsh-conversation-navigator/HEAD/assets/screenshots/02-steps.png", + "https://raw.githubusercontent.com/gjj-star/dsh-conversation-navigator/HEAD/assets/screenshots/03-no-round.png", + "https://raw.githubusercontent.com/gjj-star/dsh-conversation-navigator/HEAD/assets/screenshots/04-minimal.png", + "https://raw.githubusercontent.com/gjj-star/dsh-conversation-navigator/HEAD/assets/screenshots/05-search-n-hover.png", + "https://raw.githubusercontent.com/gjj-star/dsh-conversation-navigator/HEAD/assets/screenshots/06-dark.png", + "https://raw.githubusercontent.com/gjj-star/dsh-conversation-navigator/HEAD/assets/screenshots/07-theme1-preview.png", + "https://raw.githubusercontent.com/gjj-star/dsh-conversation-navigator/HEAD/assets/screenshots/08-theme2-preview.png" + ] + }, + { + "name": "dsh-drop-file-to-path", + "owner": "GLFzr", + "url": "https://github.com/GLFzr/dsh-drop-file-to-path", + "page": "https://awesome-dsh-plugin.com/p/GLFzr/dsh-drop-file-to-path/", + "category": "ui", + "description": { + "en": "Codex-style drag-drop: drag any file into the DSH web GUI, it lands in ~/.dsh-dropbox, and the path is inserted into the composer as a whole blue chip.", + "zh": "Codex 式拖拽:把任意文件拖入 DSH Web 界面,文件存入 ~/.dsh-dropbox,路径以整块蓝色 chip 插入输入框。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:GLFzr/dsh-drop-file-to-path", + "added": "2026-08-15" + }, + { + "name": "dsh-russian-lang", + "owner": "GooDAnDReaDY", + "url": "https://github.com/GooDAnDReaDY/dsh-russian-lang", + "page": "https://awesome-dsh-plugin.com/p/GooDAnDReaDY/dsh-russian-lang/", + "category": "ui", + "description": { + "en": "Adds Russian as the third option in the native Settings - General - Language menu of the DeepSeek Harness web UI, with 100% coverage of the 30 DSH core namespaces (722 keys) plus 9 plugin namespaces (962 keys). The locale runtime snapshot is extended at runtime on unpatched cores; no files in the DSH installation are modified, and the choice is persisted through the plugin-owned russian-lang settings namespace.", + "zh": "将俄语作为第三个选项添加到 DeepSeek Harness 网页 UI 原生的「设置 - 通用 - 语言」菜单中,覆盖 30 个 DSH 核心命名空间(722 个键,覆盖率 100%)以及 9 个插件命名空间(962 个键)。在未打补丁的核心上,运行时直接扩展 locale runtime snapshot;不会修改 DSH 安装目录中的任何文件,所选语言会通过插件自有的 russian-lang 命名空间持久化保存。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:GooDAnDReaDY/dsh-russian-lang", + "added": "2026-08-26" + }, + { + "name": "arcana", + "owner": "GooodWei", + "url": "https://github.com/GooodWei/arcana", + "page": "https://awesome-dsh-plugin.com/p/GooodWei/arcana/", + "category": "ui", + "description": { + "en": "A floating command deck that lists every slash command in DeepSeek Harness as runnable buttons, sorted by usage.", + "zh": "DeepSeek Harness 的悬浮命令甲板:把所有斜杠命令列成可执行按钮,悬停看介绍,按使用次数排序。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:GooodWei/arcana", + "added": "2026-08-14" + }, + { + "name": "draw2code", + "owner": "guchang", + "url": "https://github.com/guchang/draw2code", + "page": "https://awesome-dsh-plugin.com/p/guchang/draw2code/", + "category": "ui", + "description": { + "en": "An Excalidraw prototyping tab for dsh-better-sidebar with structured product clarification, conflict-aware human and Agent board updates, and prototype-guided frontend generation.", + "zh": "运行在 dsh-better-sidebar 中的 Excalidraw 协作原型插件:结构化澄清产品需求、保护用户手工修改的人机画板更新,以及基于原型的前端生成。" + }, + "npm": null, + "tarball": "https://github.com/guchang/draw2code/releases/latest/download/draw2code.tgz", + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/guchang/draw2code/releases/latest/download/draw2code.tgz\"", + "added": "2026-08-20" + }, + { + "name": "dsh-interrupt-button", + "owner": "guo-ziao", + "url": "https://github.com/guo-ziao/dsh-interrupt-button", + "page": "https://awesome-dsh-plugin.com/p/guo-ziao/dsh-interrupt-button/", + "category": "ui", + "description": { + "en": "A green interrupt button beside the send box: silently stops a running agent, which then summarizes its progress and asks for your new requirements, with a customizable interrupt prompt.", + "zh": "发送按钮旁的绿色打断按钮:运行时一键静默中断 AI,AI 停止后总结进展并向你征求新要求,支持自定义打断提示词。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:guo-ziao/dsh-interrupt-button", + "added": "2026-08-15" + }, + { + "name": "dsh-game-hud", + "owner": "guoliyuan97-png", + "url": "https://github.com/guoliyuan97-png/dsh-game-hud", + "page": "https://awesome-dsh-plugin.com/p/guoliyuan97-png/dsh-game-hud/", + "category": "ui", + "description": { + "en": "Game-style floating HUD: balance HP bar, context MP bar, official peak/valley pricing with countdown, auto-compaction under 5% remaining, and memory-carrying new conversation.", + "zh": "游戏风格悬浮 HUD:余额血条、上下文蓝条、官方峰谷定价与切换倒计时、上下文不足5%自动压缩、携带记忆开启新对话。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:guoliyuan97-png/dsh-game-hud", + "added": "2026-08-22" + }, + { + "name": "dsh-sticky-disclosure", + "owner": "Han-1413141", + "url": "https://github.com/Han-1413141/dsh-sticky-disclosure", + "page": "https://awesome-dsh-plugin.com/p/Han-1413141/dsh-sticky-disclosure/", + "category": "ui", + "description": { + "en": "One-click collapse of every expanded section (Think rows, tool cards) with a live-count pill and a customizable hotkey.", + "zh": "一键收起会话中所有展开的区块(Think、工具卡等),常驻计数按钮 + 自定义快捷键。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:Han-1413141/dsh-sticky-disclosure", + "added": "2026-08-14" + }, + { + "name": "dsh-ui-hub", + "owner": "Han-1413141", + "url": "https://github.com/Han-1413141/dsh-ui-hub", + "page": "https://awesome-dsh-plugin.com/p/Han-1413141/dsh-ui-hub/", + "category": "ui", + "description": { + "en": "UI butler for every plugin UI: official/plugin categories, per-widget toggles, drag move/resize, collision avoidance and one-click auto arrange.", + "zh": "UI 管家:官方/插件 UI 分区折叠、逐条开关,拖拽移动/改大小,碰撞避让与一键自动排布。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:Han-1413141/dsh-ui-hub", + "added": "2026-08-15" + }, + { + "name": "dsh-bubble-explain", + "owner": "Hanmiao33", + "url": "https://github.com/Hanmiao33/dsh-bubble-explain", + "page": "https://awesome-dsh-plugin.com/p/Hanmiao33/dsh-bubble-explain/", + "category": "ui", + "description": { + "en": "Select any text in a conversation and tap the Explain button to get a streaming Markdown explanation bubble, with recursive follow-up questions.", + "zh": "对话中框选任意文字后点击「解释」按钮,弹出 Markdown 实时流式解释气泡,支持递归追问。" + }, + "npm": null, + "tarball": "https://github.com/Hanmiao33/dsh-bubble-explain/releases/latest/download/dsh-external-bubble-explain.tgz", + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/Hanmiao33/dsh-bubble-explain/releases/latest/download/dsh-external-bubble-explain.tgz\"", + "added": "2026-08-24" + }, + { + "name": "dsh-diff-stat", + "owner": "HaoyueQin", + "url": "https://github.com/HaoyueQin/dsh-diff-stat", + "page": "https://awesome-dsh-plugin.com/p/HaoyueQin/dsh-diff-stat/", + "category": "ui", + "description": { + "en": "Inline +N −M badges on edit/write tool rows and a collapsible per-turn file-change summary card with aligned diffs, review and undo — covers native edit/write, str_replace_editor and Code Dispatch sub-calls, no git required.", + "zh": "在编辑/写入工具行内联 +N −M 徽标,每轮末尾给出可折叠的文件变更汇总卡:对齐差异、逐文件审查与撤销;覆盖原生 edit/write、str_replace_editor 与 Code Dispatch 子调用,不依赖 git。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:HaoyueQin/dsh-diff-stat", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/HaoyueQin/dsh-diff-stat/HEAD/docs/images/showcase/showcase-turn-card.png", + "https://raw.githubusercontent.com/HaoyueQin/dsh-diff-stat/HEAD/docs/images/showcase/showcase-parallel-write-diff.png" + ] + }, + { + "name": "dsh-at-any", + "owner": "hatsuyuki0103", + "url": "https://github.com/hatsuyuki0103/dsh-at-any", + "page": "https://awesome-dsh-plugin.com/p/hatsuyuki0103/dsh-at-any/", + "category": "ui", + "description": { + "en": "Codex-style `@file` mentions for the web GUI: search and reference workspace files of every format (.java, .vue, PDF, images, extension-less, hidden) with no index truncation. Replaces dsh-at-file.", + "zh": "Codex 风格的 `@file` 文件引用:输入框里搜索并引用工作区所有格式的文件(.java、.vue、PDF、图片、无扩展名、隐藏文件),无索引上限。dsh-at-file 的替代品。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:hatsuyuki0103/dsh-at-any", + "added": "2026-08-19" + }, + { + "name": "dsh-plugin-response-window", + "owner": "heiheiha798", + "url": "https://github.com/heiheiha798/dsh-plugin-response-window", + "page": "https://awesome-dsh-plugin.com/p/heiheiha798/dsh-plugin-response-window/", + "category": "ui", + "description": { + "en": "Web UI plugin that folds each staged response's think and tool calls into per-segment bounded, scrollable slide windows with a configurable height, keeping every intermediate response visible.", + "zh": "Web UI 插件:以阶段性文字回复为界分段,把每段的 think 与工具调用收进限高可滚动、高度可配置的 slide 窗口,中间过程始终可见。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:heiheiha798/dsh-plugin-response-window", + "added": "2026-08-27" + }, + { + "name": "dsh-recall-unread", + "owner": "hg1048596-pixel", + "url": "https://github.com/hg1048596-pixel/dsh-recall-unread", + "page": "https://awesome-dsh-plugin.com/p/hg1048596-pixel/dsh-recall-unread/", + "category": "ui", + "description": { + "en": "Adds a strip above the composer to recall sent-but-unread (steering) text messages before the model reads them.", + "zh": "在输入框上方提供「未读消息」条带,可在模型读取前撤回已发送但尚未被读取的文字消息。" + }, + "npm": "dsh-recall-unread", + "stars": 60, + "downloads": 174, + "install": "dsh plugin --profile web add dsh-recall-unread", + "added": "2026-08-18" + }, + { + "name": "dsh-file-upload", + "owner": "HongMing-Huang", + "url": "https://github.com/HongMing-Huang/dsh-file-upload", + "page": "https://awesome-dsh-plugin.com/p/HongMing-Huang/dsh-file-upload/", + "category": "ui", + "description": { + "en": "Claude-style drag-and-drop/paperclip file upload with content sniffing, document-to-Markdown via Microsoft MarkItDown (built-in JS fallback), text inlining, and a read_document tool for agents.", + "zh": "Claude 风格拖拽/回形针文件上传:内容嗅探、文档转 Markdown(微软 MarkItDown,内置 JS 兜底)、文本直插输入框、read_document 工具。" + }, + "npm": "dsh-file-upload", + "stars": 16, + "downloads": 4555, + "install": "dsh plugin --profile web add dsh-file-upload", + "added": "2026-08-15" + }, + { + "name": "dsh-timeline", + "owner": "houyanchao", + "url": "https://github.com/houyanchao/dsh-timeline", + "page": "https://awesome-dsh-plugin.com/p/houyanchao/dsh-timeline/", + "category": "ui", + "description": { + "en": "DSH productivity plugin: timeline navigation, prompt library, conversation export, quick notes, and more.", + "zh": "DSH 效率插件:时间轴、提示词库、导出对话、闪记等提效功能。" + }, + "npm": "dsh-timeline", + "stars": 22, + "downloads": 867, + "install": "dsh plugin --profile web add dsh-timeline", + "added": "2026-08-18" + }, + { + "name": "dsh-auto-continue", + "owner": "HsiangNianian", + "url": "https://github.com/HsiangNianian/dsh-auto-continue", + "page": "https://awesome-dsh-plugin.com/p/HsiangNianian/dsh-auto-continue/", + "category": "ui", + "description": { + "en": "Auto-resumes interrupted DSH Web requests: sends a queued 「继续」 after network, timeout or host-crash failures, with error classification, adaptive backoff, templated continue text and browser notifications.", + "zh": "DSH Web 请求中断自动续跑:网络、超时或宿主崩溃等非人为失败后自动发送「继续」,支持错误分类、自适应退避、模板化继续文本与浏览器通知。" + }, + "npm": "dsh-client-auto-continue", + "stars": 39, + "downloads": 7867, + "install": "dsh plugin --profile web add dsh-client-auto-continue", + "added": "2026-08-14" + }, + { + "name": "dsh-user-message-timeline", + "owner": "huang-chunc", + "url": "https://github.com/huang-chunc/dsh-user-message-timeline", + "page": "https://awesome-dsh-plugin.com/p/huang-chunc/dsh-user-message-timeline/", + "category": "ui", + "description": { + "en": "User Message Timeline - floating pill rail with hover preview, click/drag jump and pagination, switchable left/right.", + "zh": "对话内悬浮药丸导轨:等距药丸、悬停预览、点击/拖拽跳转、分页自动加载,开关在设置-插件-插件配置。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:huang-chunc/dsh-user-message-timeline", + "added": "2026-08-27" + }, + { + "name": "dsh-sidebar-assistant", + "owner": "hufeide", + "url": "https://github.com/hufeide/dsh-sidebar-assistant", + "page": "https://awesome-dsh-plugin.com/p/hufeide/dsh-sidebar-assistant/", + "category": "ui", + "description": { + "en": "A sidebar assistant plugin for the Web interface, allows users to select any text, displays the model’s reasoning process and final answer in a floating panel.", + "zh": "Web 界面的侧边栏助手插件,在 Web 聊天界面里选中任意文本,在浮层面板里流式显示思考过程与最终答案。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:hufeide/dsh-sidebar-assistant", + "added": "2026-08-22" + }, + { + "name": "dsh-tianshu-tui", + "owner": "huiliyi37", + "url": "https://github.com/huiliyi37/dsh-tianshu-tui", + "page": "https://awesome-dsh-plugin.com/p/huiliyi37/dsh-tianshu-tui/", + "category": "ui", + "description": { + "en": "A terminal UI (TUI) for DeepSeek Harness.", + "zh": "DeepSeek Harness 的终端 UI(TUI)。" + }, + "npm": "@huiliyi37/dsh-tianshu-tui", + "stars": 234, + "downloads": 2753, + "install": "dsh plugin --profile web add @huiliyi37/dsh-tianshu-tui", + "added": "2026-08-13" + }, + { + "name": "dsh-whale-desktop-launcher", + "owner": "HUITianYi", + "url": "https://github.com/HUITianYi/dsh-whale-desktop-launcher", + "page": "https://awesome-dsh-plugin.com/p/HUITianYi/dsh-whale-desktop-launcher/", + "category": "ui", + "description": { + "en": "Windows desktop launcher for DSH with a whale-girl icon, background server startup, and a Chromium app-mode window without browser chrome.", + "zh": "DSH 的 Windows 桌面启动器,提供鲸鱼娘图标、后台服务启动和无浏览器工具栏的 Chromium 应用窗口。" + }, + "npm": "dsh-whale-desktop-launcher", + "tarball": "https://github.com/HUITianYi/dsh-whale-desktop-launcher/releases/latest/download/dsh-whale-desktop-launcher-0.1.0.tgz", + "stars": 7, + "downloads": 174, + "install": "dsh plugin --profile web add dsh-whale-desktop-launcher", + "added": "2026-08-18" + }, + { + "name": "dsh-s7r", + "owner": "hunter118", + "url": "https://github.com/hunter118/dsh-s7r", + "page": "https://awesome-dsh-plugin.com/p/hunter118/dsh-s7r/", + "category": "ui", + "description": { + "en": "A retro macOS System 7-style workstation UI for DeepSeek Harness with desktop windows, Workspace and Agent management, Finder, Terminal, Monitor, context tools, and desk accessories.", + "zh": "为 DeepSeek Harness 提供复古 macOS System 7 风格工作站界面,包含桌面窗口、工作区与 Agent 管理、Finder、终端、监视器、上下文工具和桌面附件。" + }, + "npm": null, + "tarball": "https://github.com/hunter118/dsh-s7r/releases/download/v0.9.3/dsh-s7r-0.9.3.tgz", + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/hunter118/dsh-s7r/releases/download/v0.9.3/dsh-s7r-0.9.3.tgz\"", + "added": "2026-08-21", + "screenshots": [ + "https://raw.githubusercontent.com/hunter118/dsh-s7r/HEAD/docs/screenshots/s7r-desktop.png", + "https://raw.githubusercontent.com/hunter118/dsh-s7r/HEAD/docs/screenshots/s7r-agent.png", + "https://raw.githubusercontent.com/hunter118/dsh-s7r/HEAD/docs/screenshots/s7r-workflows.png", + "https://raw.githubusercontent.com/hunter118/dsh-s7r/HEAD/docs/screenshots/s7r-display.png" + ] + }, + { + "name": "dsh-plugin-open-with", + "owner": "hyrinx", + "url": "https://github.com/hyrinx/dsh-plugin-open-with", + "page": "https://awesome-dsh-plugin.com/p/hyrinx/dsh-plugin-open-with/", + "category": "ui", + "description": { + "en": "Adds a capsule split-button to the conversation header: launch VS Code, terminal, or File Explorer at the current workspace directory in one click.", + "zh": "在 DeepSeek Harness Web 会话头部添加胶囊拆分按钮,一键在当前工作区打开 VS Code、终端和文件资源管理器。" + }, + "npm": "dsh-plugin-open-with", + "stars": 2, + "downloads": 332, + "install": "dsh plugin --profile web add dsh-plugin-open-with", + "added": "2026-08-24" + }, + { + "name": "dsh-client-ui-shortcuts", + "owner": "hytime", + "url": "https://github.com/hytime/dsh-client-ui-shortcuts", + "page": "https://awesome-dsh-plugin.com/p/hytime/dsh-client-ui-shortcuts/", + "category": "ui", + "description": { + "en": "Profile-aware keyboard shortcuts for DSH Web: supports Question and Approval cards, Standard/Vim/Custom profiles, and capability-gated Session and Workspace navigation.", + "zh": "为 DSH Web 提供按 profile 管理的键盘快捷键:支持 Question 和 Approval 卡片、Standard/Vim/Custom 配置,并按能力提供 Session 与 Workspace 导航。" + }, + "npm": "@hytime/dsh-client-ui-shortcuts", + "stars": 2, + "downloads": 2309, + "install": "dsh plugin --profile web add @hytime/dsh-client-ui-shortcuts", + "added": "2026-08-24" + }, + { + "name": "dsh-clean-desktop-shell", + "owner": "Icather", + "url": "https://github.com/Icather/dsh-clean-desktop-shell", + "page": "https://awesome-dsh-plugin.com/p/Icather/dsh-clean-desktop-shell/", + "category": "ui", + "description": { + "en": "Clean desktop shell for DSH as a plugin: wraps your existing web profile in a native window, tray-managed backend start/restart/stop (progress dialogs), offline auto-reconnect, desktop shortcut and single instance. Windows + macOS.", + "zh": "DSH 插件形态的纯净桌面壳:为现有 web profile 套一层原生窗口,托盘管理后端启停/重启(带进度弹窗)、离线自动重连、桌面快捷方式与单实例,Windows + macOS 双平台。" + }, + "npm": "dsh-clean-desktop-shell", + "stars": 3, + "downloads": 1050, + "install": "dsh plugin --profile web add dsh-clean-desktop-shell", + "added": "2026-08-21" + }, + { + "name": "dsh-shorts-wall", + "owner": "imkingjh999", + "url": "https://github.com/imkingjh999/dsh-shorts-wall", + "page": "https://awesome-dsh-plugin.com/p/imkingjh999/dsh-shorts-wall/", + "category": "ui", + "description": { + "en": "Adds a docked-first vertical shorts wall to the DSH Web UI with YouTube Shorts and Bilibili sources, keyword rotation, four-corner resizing, and Alt+S/Alt+M shortcuts.", + "zh": "在 DSH Web UI 中加入默认贴边的竖屏短视频墙:YouTube Shorts 与 B站双源、关键词轮换、四角缩放及 Alt+S/Alt+M 快捷键。" + }, + "npm": "dsh-shorts-wall", + "stars": 3, + "downloads": 1923, + "install": "dsh plugin --profile web add dsh-shorts-wall", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/imkingjh999/dsh-shorts-wall/main/docs/screenshot-stick.png", + "https://raw.githubusercontent.com/imkingjh999/dsh-shorts-wall/main/docs/screenshot-float.png", + "https://raw.githubusercontent.com/imkingjh999/dsh-shorts-wall/main/docs/screenshot-minimized.png" + ] + }, + { + "name": "dsh-splash-launcher", + "owner": "Isilsolme", + "url": "https://github.com/Isilsolme/dsh-splash-launcher", + "page": "https://awesome-dsh-plugin.com/p/Isilsolme/dsh-splash-launcher/", + "category": "ui", + "description": { + "en": "Windows one-click launcher with a borderless WPF startup animation (stroke-by-stroke HARNESS lettering) that starts dsh web in the background and fades into the GUI once ready, plus a desktop_launch tool.", + "zh": "Windows 一键启动器:无边框 WPF 启动动画(HARNESS 逐笔描边),后台拉起 dsh web 并在 GUI 就绪后淡入,附 desktop_launch 工具。" + }, + "npm": "dsh-splash-launcher", + "stars": 4, + "downloads": 567, + "install": "dsh plugin --profile web add dsh-splash-launcher", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/Isilsolme/dsh-splash-launcher/main/docs/screenshots/splash-light.png", + "https://raw.githubusercontent.com/Isilsolme/dsh-splash-launcher/main/docs/screenshots/splash-drawing.png", + "https://raw.githubusercontent.com/Isilsolme/dsh-splash-launcher/main/docs/screenshots/splash-dark.png", + "https://raw.githubusercontent.com/Isilsolme/dsh-splash-launcher/main/docs/screenshots/main.png" + ] + }, + { + "name": "dsh-flowglass", + "owner": "Iwctwbh", + "url": "https://github.com/Iwctwbh/dsh-flowglass", + "page": "https://awesome-dsh-plugin.com/p/Iwctwbh/dsh-flowglass/", + "category": "ui", + "description": { + "en": "Live flowgraph visualization for DeepSeek Harness sessions, including tool calls, parallel groups, subagent branches, and drill-down details.", + "zh": "DeepSeek Harness 会话实时流程图,可查看工具调用、并行分组、子代理分支和逐层详情。" + }, + "npm": "dsh-flowglass", + "stars": 12, + "downloads": 969, + "install": "dsh plugin --profile web add dsh-flowglass", + "added": "2026-08-23" + }, + { + "name": "dsh-system-monitor", + "owner": "jackma5477001", + "url": "https://github.com/jackma5477001/dsh-system-monitor", + "page": "https://awesome-dsh-plugin.com/p/jackma5477001/dsh-system-monitor/", + "category": "ui", + "description": { + "en": "Displays real-time CPU and memory usage for the machine running the DSH Web server.", + "zh": "在 DSH Web 界面右上角实时显示运行 DSH 服务端机器的 CPU 与内存使用情况。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:jackma5477001/dsh-system-monitor", + "added": "2026-08-18" + }, + { + "name": "dsh-inline-diff", + "owner": "JanEickholt", + "url": "https://github.com/JanEickholt/dsh-inline-diff", + "page": "https://awesome-dsh-plugin.com/p/JanEickholt/dsh-inline-diff/", + "category": "ui", + "description": { + "en": "Renders edit and write tool calls as always-expanded side-by-side diffs, with optional syntax coloring and word-level highlighting.", + "zh": "将编辑与写入工具调用渲染为常开的双栏 diff,可选语法配色与词级高亮。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:JanEickholt/dsh-inline-diff", + "added": "2026-08-27" + }, + { + "name": "dsh-ui-mobile", + "owner": "jasondu", + "url": "https://github.com/jasondu/dsh-ui-mobile", + "page": "https://awesome-dsh-plugin.com/p/jasondu/dsh-ui-mobile/", + "category": "ui", + "description": { + "en": "Mobile-first DeepSeek Harness UI with an installable PWA, responsive navigation, and Web Push notifications when an agent completes successfully.", + "zh": "面向手机的 DeepSeek Harness 界面,提供可安装 PWA、响应式导航,以及 Agent 成功完成时的 Web Push 通知。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:jasondu/dsh-ui-mobile", + "added": "2026-08-18" + }, + { + "name": "dsh-paste-path", + "owner": "jhuanxx44", + "url": "https://github.com/jhuanxx44/dsh-paste-path", + "page": "https://awesome-dsh-plugin.com/p/jhuanxx44/dsh-paste-path/", + "category": "ui", + "description": { + "en": "Finder clipboard paste for macOS: Cmd+C files or folders in Finder, then Ctrl+V in the DSH composer inserts their absolute paths; ordinary text paste with Cmd+V is untouched.", + "zh": "macOS Finder 剪贴板粘贴:在 Finder 里 Cmd+C 复制文件或文件夹,回到 DSH 按 Ctrl+V 把绝对路径插入输入框;普通文本粘贴的 Cmd+V 不受影响。" + }, + "npm": "dsh-paste-path", + "stars": 1, + "downloads": 521, + "install": "dsh plugin --profile web add dsh-paste-path", + "added": "2026-08-21" + }, + { + "name": "dsh-ux", + "owner": "jiangnanquan", + "url": "https://github.com/jiangnanquan/dsh-ux", + "page": "https://awesome-dsh-plugin.com/p/jiangnanquan/dsh-ux/", + "category": "ui", + "description": { + "en": "Solarized light theme, compact layout, think/tool-chain collapse capsules, and balance, session cost, and usage dashboards for the DSH web UI.", + "zh": "Solarized 浅色主题、紧凑布局、思考/工具链折叠胶囊,以及余额、本轮成本与用量看板的 DSH Web 界面增强插件。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:jiangnanquan/dsh-ux", + "added": "2026-08-14" + }, + { + "name": "dsh-canvas-preview", + "owner": "jiuyuechuwuhao", + "url": "https://github.com/jiuyuechuwuhao/dsh-canvas-preview", + "page": "https://awesome-dsh-plugin.com/p/jiuyuechuwuhao/dsh-canvas-preview/", + "category": "ui", + "description": { + "en": "Conversation Canvas tab: live-preview workspace HTML artifacts and export PNG/JPG/SVG locally (`npx dsh-canvas-preview`).", + "zh": "Conversation Canvas 标签页:实时预览工作区内的 HTML artifact,并支持本地导出 PNG/JPG/SVG(`npx dsh-canvas-preview`)。" + }, + "npm": "dsh-canvas-preview", + "stars": 3, + "downloads": 1193, + "install": "dsh plugin --profile web add dsh-canvas-preview", + "added": "2026-08-18" + }, + { + "name": "dsh-workspace-explorer", + "owner": "Jiyr0119", + "url": "https://github.com/Jiyr0119/dsh-workspace-explorer", + "page": "https://awesome-dsh-plugin.com/p/Jiyr0119/dsh-workspace-explorer/", + "category": "ui", + "description": { + "en": "Standalone workspace file-tree panel for the DSH web UI: a session-header icon opens an animated popup of the current workspace tree; click or drag a file to insert its reference into the composer, with search, inline preview, live settings, and zh/en i18n; single-purpose, zero-config, one-command install.", + "zh": "独立单功能的工作区文件树面板:会话头部图标打开当前工作区目录树的动画弹窗;点击或拖拽文件即可把引用插入输入框;自带搜索、行内预览、实时设置与中英双语;零配置,一条命令即装即用。" + }, + "npm": "@jiyr0119/dsh-workspace-explorer", + "stars": 8, + "downloads": 1959, + "install": "dsh plugin --profile web add @jiyr0119/dsh-workspace-explorer", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/Jiyr0119/dsh-workspace-explorer/main/assets/screenshots/panel.png", + "https://raw.githubusercontent.com/Jiyr0119/dsh-workspace-explorer/main/assets/screenshots/tree.png", + "https://raw.githubusercontent.com/Jiyr0119/dsh-workspace-explorer/main/assets/screenshots/insert.png" + ] + }, + { + "name": "dsh-chat-timeline", + "owner": "jjxjjjjiik-bot", + "url": "https://github.com/jjxjjjjiik-bot/dsh-chat-timeline", + "page": "https://awesome-dsh-plugin.com/p/jjxjjjjiik-bot/dsh-chat-timeline/", + "category": "ui", + "description": { + "en": "1:1 port of DeepSeek's official web right-side chat navigation rail (ScrollNav): hover-expandable rail, reading-position highlight, click-to-jump.", + "zh": "1:1 复刻 DeepSeek 官网右侧对话导航栏(ScrollNav):悬停展开面板、阅读位置高亮、点击跳转。" + }, + "npm": "dsh-chat-timeline", + "stars": 33, + "downloads": 1245, + "install": "dsh plugin --profile web add dsh-chat-timeline", + "added": "2026-08-14" + }, + { + "name": "dsh-liquid-glass-input", + "owner": "jkamkk", + "url": "https://github.com/jkamkk/dsh-liquid-glass-input", + "page": "https://awesome-dsh-plugin.com/p/jkamkk/dsh-liquid-glass-input/", + "category": "ui", + "description": { + "en": "Liquid Glass refraction input card for the DSH web GUI, with coupled-spring press animation. Published on npm: dsh plugin --profile web add dsh-liquid-glass-input.", + "zh": "给 DSH Web GUI 输入卡加上液态玻璃折射效果与弹簧按压动画。已发布 npm:dsh plugin --profile web add dsh-liquid-glass-input。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:jkamkk/dsh-liquid-glass-input", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/jkamkk/dsh-liquid-glass-input/HEAD/assets/demo-input.webp", + "https://raw.githubusercontent.com/jkamkk/dsh-liquid-glass-input/HEAD/assets/preview-light.png", + "https://raw.githubusercontent.com/jkamkk/dsh-liquid-glass-input/HEAD/assets/preview-dark.png" + ] + }, + { + "name": "dsh-no-setup-mode", + "owner": "johnnycls", + "url": "https://github.com/johnnycls/dsh-no-setup-mode", + "page": "https://awesome-dsh-plugin.com/p/johnnycls/dsh-no-setup-mode/", + "category": "ui", + "description": { + "en": "No-setup mode for the DSH web UI: hides advanced surfaces, auto-applies best defaults (chat mode, full access, account balance), and one-click role-play personas (maid/butler).", + "zh": "DSH 网页免设置模式:隐藏复杂界面、自动套用最佳设置(聊天模式、Full Access、账户余额),并提供一键人设角色扮演(女僕/管家)。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:johnnycls/dsh-no-setup-mode", + "added": "2026-08-16" + }, + { + "name": "dsh-mcp-manager", + "owner": "Js2Hou", + "url": "https://github.com/Js2Hou/dsh-mcp-manager", + "page": "https://awesome-dsh-plugin.com/p/Js2Hou/dsh-mcp-manager/", + "category": "ui", + "description": { + "en": "Visual MCP manager for the DSH web UI: inspect, add, remove, enable/disable MCP servers and view their live connection status from Settings → MCP.", + "zh": "DSH 网页 MCP 可视化管理插件:在「设置 → MCP」中查看/新增/删除/启停 MCP 服务器,实时显示连接状态与工具数。" + }, + "npm": null, + "stars": 15, + "downloads": null, + "install": "dsh plugin --profile web add github:Js2Hou/dsh-mcp-manager", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/Js2Hou/dsh-mcp-manager/main/assets/market-screenshot.png" + ] + }, + { + "name": "dsh-strata", + "owner": "jsdvjx", + "url": "https://github.com/jsdvjx/dsh-strata", + "page": "https://awesome-dsh-plugin.com/p/jsdvjx/dsh-strata/", + "category": "ui", + "description": { + "en": "Session minimap in the scrollbar gutter: to-scale bands coloured by event kind, anchor dots for user messages and failures, auto-loading history at the top, a hover wall of every prompt in the session, and a three-step map scale switcher.", + "zh": "滚动条槽位里的会话缩略图:按事件类型着色的等比色块、用户与失败锚点、滚到顶自动补载历史、悬停线索墙铺开全部发言,附近/中/全三档地图尺度切换。" + }, + "npm": "dsh-strata", + "stars": 24, + "downloads": 6266, + "install": "dsh plugin --profile web add dsh-strata", + "added": "2026-08-19" + }, + { + "name": "dsh-vsceditor", + "owner": "k-ying", + "url": "https://github.com/k-ying/dsh-vsceditor", + "page": "https://awesome-dsh-plugin.com/p/k-ying/dsh-vsceditor/", + "category": "ui", + "description": { + "en": "Embeds a real VS Code editor (code-server, or your local desktop VS Code via the bundled bridge extension) as a GUI tab, following agent file edits live with red/green diffs and file locking.", + "zh": "在 GUI 页签中内嵌真实的 VS Code 编辑器(code-server,或通过自带 bridge 扩展连接本机桌面版 VS Code),红绿 diff 实时跟随 Agent 的文件改动,支持文件锁定。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:k-ying/dsh-vsceditor", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/k-ying/dsh-vsceditor/HEAD/assets/screenshot-1.png" + ] + }, + { + "name": "dsh-plugin-lookatstudy", + "owner": "Kaiji-Z", + "url": "https://github.com/Kaiji-Z/dsh-plugin-lookatstudy", + "page": "https://awesome-dsh-plugin.com/p/Kaiji-Z/dsh-plugin-lookatstudy/", + "category": "ui", + "description": { + "en": "A study tab with course-map, tutor and blackboard columns that turns markdown files, local folders or GitHub repos into guided courses, with lesson unlocking, mastery tracking, spaced repetition and a tutor-designed course import.", + "zh": "学习页签(课程|导师|黑板三栏):把 markdown 文件、本地文件夹或 GitHub 仓库变成导学课程,提供课时解锁、掌握度追踪、间隔重复和导师设计式课程导入。" + }, + "npm": "dsh-plugin-lookatstudy", + "stars": 2, + "downloads": 1245, + "install": "dsh plugin --profile web add dsh-plugin-lookatstudy", + "added": "2026-08-23" + }, + { + "name": "dsh-xueqiu", + "owner": "kangjinghang", + "url": "https://github.com/kangjinghang/dsh-xueqiu", + "page": "https://awesome-dsh-plugin.com/p/kangjinghang/dsh-xueqiu/", + "category": "ui", + "description": { + "en": "Xueqiu (Snowball) market panel docked above the composer: A-share/HK/US quotes and indices, watchlist, candlestick K-line with volume, MAs and crosshair tooltip, hot list, search, 7x24 news, plus a draggable mini index badge — no login needed.", + "zh": "雪球行情面板:嵌入输入框上方,免登录查看 A股/港美股行情、指数与自选股,K线蜡烛图(成交量/均线/十字光标)、热榜、搜索、7×24 快讯,另有可拖动迷你指数徽章。" + }, + "npm": "dsh-xueqiu", + "stars": 8, + "downloads": 6214, + "install": "dsh plugin --profile web add dsh-xueqiu", + "added": "2026-08-17" + }, + { + "name": "dsh-goal-mode-enhance", + "owner": "KarlOfLaw", + "url": "https://github.com/KarlOfLaw/dsh-goal-mode-enhance", + "page": "https://awesome-dsh-plugin.com/p/KarlOfLaw/dsh-goal-mode-enhance/", + "category": "ui", + "description": { + "en": "Visual goal lifecycle for the DSH web UI: a composer dock to create, multiline-edit, pause, resume, complete and clear session goals with live round progress, plus a phase-dot composer entry and a settings-page goal overview.", + "zh": "为 DSH Web 界面提供可视化的会话目标生命周期:composer 上方的目标栏支持创建、多行编辑、暂停、恢复、完成与清除并实时显示轮次进度,工具行入口带阶段色点,设置页提供目标总览。" + }, + "npm": null, + "tarball": "https://github.com/KarlOfLaw/dsh-goal-mode-enhance/releases/download/v0.1.0/dsh-goal-mode-0.1.0.tgz", + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/KarlOfLaw/dsh-goal-mode-enhance/releases/download/v0.1.0/dsh-goal-mode-0.1.0.tgz\"", + "added": "2026-08-24", + "screenshots": [ + "https://raw.githubusercontent.com/KarlOfLaw/dsh-goal-mode-enhance/HEAD/assets/screenshot-goal-dock.png", + "https://raw.githubusercontent.com/KarlOfLaw/dsh-goal-mode-enhance/HEAD/assets/screenshot-goal-create.png", + "https://raw.githubusercontent.com/KarlOfLaw/dsh-goal-mode-enhance/HEAD/assets/screenshot-goal-chip.png" + ] + }, + { + "name": "dsh-side-chat", + "owner": "KarlOfLaw", + "url": "https://github.com/KarlOfLaw/dsh-side-chat", + "page": "https://awesome-dsh-plugin.com/p/KarlOfLaw/dsh-side-chat/", + "category": "ui", + "description": { + "en": "Native side-by-side chat beside the main conversation: an independent archived DSH session with full native UI, on-demand parent-context retrieval, selection quoting, and a keep-or-delete lifecycle.", + "zh": "在主对话旁边打开原生并排侧聊:独立归档的真实 DSH 会话、按需读取父会话上下文、选文引用与保留或删除的生命周期。" + }, + "npm": null, + "tarball": "https://github.com/KarlOfLaw/dsh-side-chat/releases/latest/download/dsh-side-chat-1.2.0.tgz", + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/KarlOfLaw/dsh-side-chat/releases/latest/download/dsh-side-chat-1.2.0.tgz\"", + "added": "2026-08-24", + "screenshots": [ + "https://raw.githubusercontent.com/KarlOfLaw/dsh-side-chat/main/docs/assets/dsh-side-chat-hero.png", + "https://raw.githubusercontent.com/KarlOfLaw/dsh-side-chat/main/docs/assets/side-chat-parallel.png", + "https://raw.githubusercontent.com/KarlOfLaw/dsh-side-chat/main/docs/assets/side-chat-selection.png", + "https://raw.githubusercontent.com/KarlOfLaw/dsh-side-chat/main/docs/assets/side-chat-settings.png" + ] + }, + { + "name": "dsh-navigation-bar", + "owner": "kelearns", + "url": "https://github.com/kelearns/dsh-navigation-bar", + "page": "https://awesome-dsh-plugin.com/p/kelearns/dsh-navigation-bar/", + "category": "ui", + "description": { + "en": "Piano-key in-conversation navigation for the Web UI: one key per user message with hover ladder, message-preview tooltip, and active-message highlight; click to smooth-scroll.", + "zh": "钢琴键风格会话内导航条:一根键锚定一条用户消息,悬停显示消息预览气泡与阶梯展开,点击平滑跳转。" + }, + "npm": "@kelearns/dsh-navigation-bar", + "stars": 9, + "downloads": 571, + "install": "dsh plugin --profile web add @kelearns/dsh-navigation-bar", + "added": "2026-08-15" + }, + { + "name": "dsh-task-graph", + "owner": "KevinZhangNothing", + "url": "https://github.com/KevinZhangNothing/dsh-task-graph", + "page": "https://awesome-dsh-plugin.com/p/KevinZhangNothing/dsh-task-graph/", + "category": "ui", + "description": { + "en": "Adds a Graph tab that renders one session's execution as an interactive DAG of turns, LLM steps, tools, skills, subagents, code changes and tests, with live status, retry aggregation, critical path and trajectory linkage.", + "zh": "在会话区新增「图谱」Tab,把单个会话的执行过程渲染为可交互 DAG:轮次、LLM 步骤、工具、技能、子代理、代码改动与测试,支持实时状态、重试聚合、关键路径与轨迹双向联动。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:KevinZhangNothing/dsh-task-graph", + "added": "2026-08-28" + }, + { + "name": "dsh-balance", + "owner": "kkbsgg", + "url": "https://github.com/kkbsgg/dsh-balance", + "page": "https://awesome-dsh-plugin.com/p/kkbsgg/dsh-balance/", + "category": "ui", + "description": { + "en": "Shows the selected model provider's account balance next to the composer's model seat once a model and reasoning effort are selected.", + "zh": "在选择模型与推理强度后,于输入框模型选择器旁显示当前模型提供方的账户余额。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:kkbsgg/dsh-balance", + "added": "2026-08-26" + }, + { + "name": "dsh-nav-pointer", + "owner": "kongdexu", + "url": "https://github.com/kongdexu/dsh-nav-pointer", + "page": "https://awesome-dsh-plugin.com/p/kongdexu/dsh-nav-pointer/", + "category": "ui", + "description": { + "en": "Vertical message pointer rail on the left edge of the chat: one dash per user message, click to scroll to that message, hover for a preview bubble.", + "zh": "聊天区左侧的消息指针导轨:每条用户消息一个横条,点击滚动定位,悬停显示预览气泡。" + }, + "npm": "dsh-nav-pointer", + "stars": 2, + "downloads": 790, + "install": "dsh plugin --profile web add dsh-nav-pointer", + "added": "2026-08-20" + }, + { + "name": "dsh-tui-app", + "owner": "kouyichi", + "url": "https://github.com/kouyichi/dsh-tui-app", + "page": "https://awesome-dsh-plugin.com/p/kouyichi/dsh-tui-app/", + "category": "ui", + "description": { + "en": "Interactive terminal chat app for dsh: streaming conversation, tool cards, jobs panel, full-text search, trajectory replay, multi-session tabs, and A2A dispatch (Ink-based).", + "zh": "dsh 交互式终端聊天应用:流式对话、工具卡片、任务面板、全文搜索、轨迹回放、多会话标签与 A2A 派发(Ink 实现)。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:kouyichi/dsh-tui-app", + "added": "2026-08-17" + }, + { + "name": "dsh-sidebar-branch-chat", + "owner": "kuanfu0430", + "url": "https://github.com/kuanfu0430/dsh-sidebar-branch-chat", + "page": "https://awesome-dsh-plugin.com/p/kuanfu0430/dsh-sidebar-branch-chat/", + "category": "ui", + "description": { + "en": "Adds a Branch Chat tab to dsh-better-sidebar that opens independent archived sessions with a head/middle/tail context digest and the same tools as the main agent.", + "zh": "在 dsh-better-sidebar 新增分支对话页签:开出独立封存 session,注入头/中/尾脉络摘要,并使用与主对话相同的工具。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:kuanfu0430/dsh-sidebar-branch-chat", + "added": "2026-08-27" + }, + { + "name": "dsh-file-uploads", + "owner": "l541402398", + "url": "https://github.com/l541402398/dsh-file-uploads", + "page": "https://awesome-dsh-plugin.com/p/l541402398/dsh-file-uploads/", + "category": "ui", + "description": { + "en": "Upload arbitrary local files from the Web composer, show pending cards, and manage stored files in Settings.", + "zh": "从 Web 输入框上传任意本地文件,以待发送卡片展示,并在设置中管理已存文件。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:l541402398/dsh-file-uploads", + "added": "2026-08-14" + }, + { + "name": "dsh-model-accordion", + "owner": "lakabuji-arch", + "url": "https://github.com/lakabuji-arch/dsh-model-accordion", + "page": "https://awesome-dsh-plugin.com/p/lakabuji-arch/dsh-model-accordion/", + "category": "ui", + "description": { + "en": "Provider-folded model selector for the DSH Web composer with catalog-driven reasoning effort choices.", + "zh": "为 DSH Web 输入栏提供按 provider 折叠的模型选择器,并使用模型目录中的真实推理档位。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:lakabuji-arch/dsh-model-accordion", + "added": "2026-08-24" + }, + { + "name": "dsh-smooth-stream", + "owner": "Laplace-bit", + "url": "https://github.com/Laplace-bit/dsh-smooth-stream", + "page": "https://awesome-dsh-plugin.com/p/Laplace-bit/dsh-smooth-stream/", + "category": "ui", + "description": { + "en": "Fluid streaming rendering and smooth scrolling for the DeepSeek Harness Web UI.", + "zh": "为 DeepSeek Harness Web UI 提供流畅流式渲染和丝滑滚动。" + }, + "npm": "dsh-smooth-stream", + "stars": 56, + "downloads": 2342, + "install": "dsh plugin --profile web add dsh-smooth-stream", + "added": "2026-08-16" + }, + { + "name": "dsh-composer-layout", + "owner": "lavapapa", + "url": "https://github.com/lavapapa/dsh-composer-layout", + "page": "https://awesome-dsh-plugin.com/p/lavapapa/dsh-composer-layout/", + "category": "ui", + "description": { + "en": "Web Composer placement for long prompts: keep it at the bottom or dock it in a resizable right column beside a long answer.", + "zh": "面向长提示词的 Web Composer 布局:输入框可保留在底部,也可停靠到可调宽度的右栏,与长回答并排阅读和写作。" + }, + "npm": "dsh-composer-layout", + "stars": 3, + "downloads": 747, + "install": "dsh plugin --profile web add dsh-composer-layout", + "added": "2026-08-20", + "screenshots": [ + "https://raw.githubusercontent.com/lavapapa/dsh-composer-layout/main/assets/screenshots/hero-en.png", + "https://raw.githubusercontent.com/lavapapa/dsh-composer-layout/main/assets/screenshots/hero-zh.png" + ] + }, + { + "name": "dsh-prompt-enhancer", + "owner": "LCQ-1024", + "url": "https://github.com/LCQ-1024/dsh-prompt-enhancer", + "page": "https://awesome-dsh-plugin.com/p/LCQ-1024/dsh-prompt-enhancer/", + "category": "ui", + "description": { + "en": "Adds a prompt-enhancement button to the DSH composer that rewrites drafts into agent-ready prompts.", + "zh": "在 DSH 输入框添加提示词增强按钮,将草稿改写为可直接交给 Agent 执行的提示词。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:LCQ-1024/dsh-prompt-enhancer", + "added": "2026-08-19" + }, + { + "name": "dsh-skills-hub", + "owner": "lcthe", + "url": "https://github.com/lcthe/dsh-skills-hub", + "page": "https://awesome-dsh-plugin.com/p/lcthe/dsh-skills-hub/", + "category": "ui", + "description": { + "en": "DSH Skill Manager: browse, enable/disable, import from Codex/Claude Code/ZCode/WorkBuddy/QCoderWork, auto-discover skills with symlink support.", + "zh": "DSH 技能管理器:浏览、启用/禁用、从 Codex/Claude Code/ZCode/WorkBuddy/QCoderWork 导入,自动发现技能并支持软链接。" + }, + "npm": "@lcthe/dsh-skills-hub", + "stars": 2, + "downloads": 706, + "install": "dsh plugin --profile web add @lcthe/dsh-skills-hub", + "added": "2026-08-23" + }, + { + "name": "dsh-timeline-rail", + "owner": "lcthe", + "url": "https://github.com/lcthe/dsh-timeline-rail", + "page": "https://awesome-dsh-plugin.com/p/lcthe/dsh-timeline-rail/", + "category": "ui", + "description": { + "en": "Message timeline rail for DeepSeek Harness web chat: evenly spaced ticks for each user message on the right edge, click to jump, hover to preview.", + "zh": "DeepSeek Harness 网页会话消息时间轴导航条:消息区右侧等间距刻度,点击跳转,悬浮预览。" + }, + "npm": "@lcthe/dsh-timeline-rail", + "stars": 2, + "downloads": 2167, + "install": "dsh plugin --profile web add @lcthe/dsh-timeline-rail", + "added": "2026-08-19" + }, + { + "name": "dsh-whale-animation", + "owner": "LeemanCheung", + "url": "https://github.com/LeemanCheung/dsh-whale-animation", + "page": "https://awesome-dsh-plugin.com/p/LeemanCheung/dsh-whale-animation/", + "category": "ui", + "description": { + "en": "A 60-frame theme-aware monochrome whale-dive animation with reactive water for the DSH Web turn status: embedded assets, seamless closed-loop playback, a reduced-motion PNG fallback, and lifecycle-clean removal.", + "zh": "DSH Web 回合状态旁的 60 帧随主题适配的单色鲸鱼深潜动画:传播式水面、无缝闭环、资源内嵌、减少动态效果 PNG 回退,且随生命周期完整清理。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:LeemanCheung/dsh-whale-animation", + "added": "2026-08-14" + }, + { + "name": "dsh-effort-switcher", + "owner": "lemonorangeapple", + "url": "https://github.com/lemonorangeapple/dsh-effort-switcher", + "page": "https://awesome-dsh-plugin.com/p/lemonorangeapple/dsh-effort-switcher/", + "category": "ui", + "description": { + "en": "A Codex-like effort switcher for Deepseek Harness.", + "zh": "为DSH添加一个类似于Codex的推理强度切换器。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:lemonorangeapple/dsh-effort-switcher", + "added": "2026-08-20", + "screenshots": [ + "https://raw.githubusercontent.com/lemonorangeapple/dsh-effort-switcher/master/screenshots/1.png", + "https://raw.githubusercontent.com/lemonorangeapple/dsh-effort-switcher/master/screenshots/2.png" + ] + }, + { + "name": "dsh-file-viewer", + "owner": "liguobao", + "url": "https://github.com/liguobao/dsh-file-viewer", + "page": "https://awesome-dsh-plugin.com/p/liguobao/dsh-file-viewer/", + "category": "ui", + "description": { + "en": "Read-only Web client file-preview panel with image, PDF, CSV/TSV, text, source-code, Markdown, JSON and YAML renderers, large-file range loading, and a provider API for non-local content.", + "zh": "Web 客户端只读文件预览面板,支持图片、PDF、CSV/TSV、文本、源代码、Markdown、JSON 与 YAML 渲染、大文件分段读取,以及面向非本地内容的 provider API。" + }, + "npm": "dsh-file-viewer", + "stars": 3, + "downloads": 964, + "install": "dsh plugin --profile web add dsh-file-viewer", + "added": "2026-08-21" + }, + { + "name": "dsh-moon-light", + "owner": "lihang-lh", + "url": "https://github.com/lihang-lh/dsh-moon-light", + "page": "https://awesome-dsh-plugin.com/p/lihang-lh/dsh-moon-light/", + "category": "ui", + "description": { + "en": "A marquee mood-light ring around the DSH web UI edge that reacts to session state, turning green while the agent works, pink when done, and yellow when something needs attention, with colors, ring width, rotation speed, and flash frequency configurable from the settings panel.", + "zh": "DSH Web 界面边缘的跑马灯氛围光环:随会话状态自动变色(运行中绿色流动、完成粉色、有待处理黄色警告),设置面板可调颜色、光圈宽度、转速与闪动频率。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:lihang-lh/dsh-moon-light", + "added": "2026-08-19" + }, + { + "name": "dsh-task-panel", + "owner": "lihang-lh", + "url": "https://github.com/lihang-lh/dsh-task-panel", + "page": "https://awesome-dsh-plugin.com/p/lihang-lh/dsh-task-panel/", + "category": "ui", + "description": { + "en": "Task panel for the DSH Web sidebar: publish tasks, auto-distill related sessions, and drive plan/develop/review stages with subagents plus in-panel acceptance.", + "zh": "DSH Web 侧边栏任务面板:发布任务、自动蒸馏历史会话,由子代理分阶段(规划/开发/复核)执行,并在面板内验收。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:lihang-lh/dsh-task-panel", + "added": "2026-08-24" + }, + { + "name": "dsh-question-index", + "owner": "lijinhao315", + "url": "https://github.com/lijinhao315/dsh-question-index", + "page": "https://awesome-dsh-plugin.com/p/lijinhao315/dsh-question-index/", + "category": "ui", + "description": { + "en": "Question index: an ordered, collapsible list of your asked questions floating on the right side of the conversation; click a question to jump the session to that point.", + "zh": "提问索引:在会话右侧按序展示你提过的问题列表,可收起;点击某条即可把会话滚动跳转到对应的提问位置。" + }, + "npm": null, + "stars": 0, + "downloads": null, + "install": "dsh plugin --profile web add github:lijinhao315/dsh-question-index", + "added": "2026-08-25" + }, + { + "name": "dsh-chat-outline", + "owner": "liliuCourier", + "url": "https://github.com/liliuCourier/dsh-chat-outline", + "page": "https://awesome-dsh-plugin.com/p/liliuCourier/dsh-chat-outline/", + "category": "ui", + "description": { + "en": "Persistent conversation outline: per-turn questions and final replies, keyword filter, one-click jump, light/full history modes, and Ctrl/Shift+click to open the same position in the Trajectory view.", + "zh": "对话栏左侧常驻大纲:按轮次列出提问与最后回复,关键词过滤与一键跳转,轻便/全面两种历史模式,按住 Ctrl/Shift 单击可跳到轨迹视图同一位置。" + }, + "npm": "dsh-chat-outline", + "stars": 8, + "downloads": 1398, + "install": "dsh plugin --profile web add dsh-chat-outline", + "added": "2026-08-14" + }, + { + "name": "DSH-Right-Sidebar", + "owner": "Limitinfinitude", + "url": "https://github.com/Limitinfinitude/DSH-Right-Sidebar", + "page": "https://awesome-dsh-plugin.com/p/Limitinfinitude/DSH-Right-Sidebar/", + "category": "ui", + "description": { + "en": "DSH Web right-side output dock that collects session artifacts, previews user-facing files, and preserves tab state per session.", + "zh": "DSH Web 原生右侧产物栏,按会话收集用户产物,预览用户可读文件并保存标签状态。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:Limitinfinitude/DSH-Right-Sidebar", + "added": "2026-08-16" + }, + { + "name": "dsh-writing-pad", + "owner": "linhx1999", + "url": "https://github.com/linhx1999/dsh-writing-pad", + "page": "https://awesome-dsh-plugin.com/p/linhx1999/dsh-writing-pad/", + "category": "ui", + "description": { + "en": "Session-scoped Markdown writing pad docked beside the conversation, with edit/preview modes, selection-based AI rewrites, diff review, undo, and draft recovery.", + "zh": "停靠在对话旁的会话级 Markdown 写作板,支持编辑/预览、选区 AI 改写、Diff 审核、撤销与草稿恢复。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:linhx1999/dsh-writing-pad", + "added": "2026-08-16" + }, + { + "name": "dsh-status-card", + "owner": "liqiming-whu", + "url": "https://github.com/liqiming-whu/dsh-status-card", + "page": "https://awesome-dsh-plugin.com/p/liqiming-whu/dsh-status-card/", + "category": "ui", + "description": { + "en": "Adds a configurable dsh-ui status card before each DSH agent reply, with bilingual templates and a live settings preview. Requires @omdsh-dev/dsh-genui from dsh-market; without it, the card markup is emitted unrendered.", + "zh": "为每次 DSH Agent 回复添加可配置的 dsh-ui 状态卡片,提供双语模板和实时设置预览。需要先从 dsh-market 安装 @omdsh-dev/dsh-genui,否则卡片标记会以未渲染的形式输出。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:liqiming-whu/dsh-status-card", + "added": "2026-08-21", + "screenshots": [ + "https://raw.githubusercontent.com/liqiming-whu/dsh-status-card/main/docs/images/templates/bilingual/template-a.svg", + "https://raw.githubusercontent.com/liqiming-whu/dsh-status-card/main/docs/images/templates/bilingual/template-b.svg", + "https://raw.githubusercontent.com/liqiming-whu/dsh-status-card/main/docs/images/templates/bilingual/template-c.svg", + "https://raw.githubusercontent.com/liqiming-whu/dsh-status-card/main/docs/images/templates/bilingual/template-d.svg", + "https://raw.githubusercontent.com/liqiming-whu/dsh-status-card/main/docs/images/templates/bilingual/template-e.svg", + "https://raw.githubusercontent.com/liqiming-whu/dsh-status-card/main/docs/images/templates/bilingual/template-f.svg" + ] + }, + { + "name": "dsh-modal-enhancer", + "owner": "lisniuse", + "url": "https://github.com/lisniuse/dsh-modal-enhancer", + "page": "https://awesome-dsh-plugin.com/p/lisniuse/dsh-modal-enhancer/", + "category": "ui", + "description": { + "en": "Window-like controls for every Web UI modal: drag by title bar, eight-way resize, pin against outside clicks, maximize to the full viewport, removable backdrop blur, and per-dialog persistent state.", + "zh": "为所有 Web UI 弹窗添加窗口化操作:标题栏拖动、八向缩放、钉住防误触关闭、全屏最大化、移除背景模糊,以及按弹窗持久化的状态。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:lisniuse/dsh-modal-enhancer", + "added": "2026-08-15" + }, + { + "name": "dsh-community-hot", + "owner": "littleboylittlegirl", + "url": "https://github.com/littleboylittlegirl/dsh-community-hot", + "page": "https://awesome-dsh-plugin.com/p/littleboylittlegirl/dsh-community-hot/", + "category": "ui", + "description": { + "en": "Floating community panel for the Web UI: 24h hot topics and hot plugins TOP10 with a draggable, always-on-top button.", + "zh": "Web UI 的社区热门悬浮面板:24 小时热门话题与热门插件 TOP10,悬浮按钮可拖动置顶、点击居中展开。" + }, + "npm": "dsh-community-hot", + "stars": 2, + "downloads": 517, + "install": "dsh plugin --profile web add dsh-community-hot", + "added": "2026-08-15" + }, + { + "name": "dsh-tui-pro", + "owner": "lk251066", + "url": "https://github.com/lk251066/dsh-tui-pro", + "page": "https://awesome-dsh-plugin.com/p/lk251066/dsh-tui-pro/", + "category": "ui", + "description": { + "en": "A full-screen terminal workbench for DeepSeek Harness with a durable assistant, workspace-grouped project sessions, transcript-only scrolling, and structured thinking, tool, diff, plan, and subagent views.", + "zh": "DeepSeek Harness 全屏终端工作台,提供独立持久助手、按工作区分组的项目会话、仅聊天区滚动,以及结构化思考、工具、diff、计划和子代理视图。" + }, + "npm": "@lk251066/dsh-tui", + "stars": 3, + "downloads": 4425, + "install": "dsh plugin --profile web add @lk251066/dsh-tui", + "added": "2026-08-19" + }, + { + "name": "dsh-plugin-balance", + "owner": "lmmzss-jk", + "url": "https://github.com/lmmzss-jk/dsh-plugin-balance", + "page": "https://awesome-dsh-plugin.com/p/lmmzss-jk/dsh-plugin-balance/", + "category": "ui", + "description": { + "en": "Balance and live session usage for the DSH web UI: header balance button (left of the Session log), dropdown with account balance, real-time token breakdown (cache miss/hit, cache write, output), cache hit rate, and official-rate cost estimates with peak/off-peak auto-switch.", + "zh": "DSH Web 余额与会话用量插件:头部余额按钮(Session log 左侧),下拉展示账户余额、实时 token 明细(缓存未命中/命中、缓存写入、输出)、缓存命中率,以及按官方峰谷价自动切换的费用估算。" + }, + "npm": "@lmmzss/dsh-plugin-balance", + "stars": 2, + "downloads": 166, + "install": "dsh plugin --profile web add @lmmzss/dsh-plugin-balance", + "added": "2026-08-18" + }, + { + "name": "deepseeh-harness-ultra-slash", + "owner": "loadingvx", + "url": "https://github.com/loadingvx/deepseeh-harness-ultra-slash", + "page": "https://awesome-dsh-plugin.com/p/loadingvx/deepseeh-harness-ultra-slash/", + "category": "ui", + "description": { + "en": "Separate slash-command group in the Web UI composer: /steer injects text on the agent's next step without interrupting the turn, /new starts a blank session, /skill and /docs wrap /steer with post-turn skill or docs tasks, and custom /steer aliases are configurable in Settings.", + "zh": "在 Web UI 输入框的斜杠菜单里单独一组插件命令:/steer 在模型下一步注入内容且不打断当前回合,/new 开启空白会话,/skill 与 /docs 在 /steer 基础上附加完成后写入 skill 或 docs,并可在设置里为常用 /steer 内容配置自定义快捷命令。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:loadingvx/deepseeh-harness-ultra-slash", + "added": "2026-08-28" + }, + { + "name": "deepseek-harness-workbench-plugin", + "owner": "loadingvx", + "url": "https://github.com/loadingvx/deepseek-harness-workbench-plugin", + "page": "https://awesome-dsh-plugin.com/p/loadingvx/deepseek-harness-workbench-plugin/", + "category": "ui", + "description": { + "en": "Fills in the Web UI's missing IDE workbench: resizable chat / editor / files-and-Git columns in Conversation, multi-tab file editing with save and new-file, workspace Terminal, a file tree with name filter and open-in-external-editor, SCM (stage/commit/push/pull, branch switch, Git graph, inline diffs), a status bar, and git_* model tools.", + "zh": "为 Web UI 补齐缺失的 IDE 工作台:对话页可拖拽三栏(对话 / 编辑器 / 文件与 Git),多标签编辑(保存、新建、路径面包屑、行内 diff)、工作区 Terminal、文件树(按名筛选、用外部编辑器打开)、源代码管理(暂存/提交/推送/拉取、分支切换、提交图)、状态栏,以及 git_* 模型工具。" + }, + "npm": null, + "stars": 27, + "downloads": null, + "install": "dsh plugin --profile web add github:loadingvx/deepseek-harness-workbench-plugin", + "added": "2026-08-15" + }, + { + "name": "dsh-chat-image-lightbox", + "owner": "loyalchiiina", + "url": "https://github.com/loyalchiiina/dsh-chat-image-lightbox", + "page": "https://awesome-dsh-plugin.com/p/loyalchiiina/dsh-chat-image-lightbox/", + "category": "ui", + "description": { + "en": "Full-screen lightbox for chat images with zoom, drag pan, download, and keyboard or touch navigation.", + "zh": "聊天图片全屏查看器:点击对话中的图片打开灯箱,支持缩放、拖拽平移、滚轮缩放、文件名显示、下载保存,以及键盘方向键与移动端滑动切换。" + }, + "npm": "@loyalchiiina/dsh-chat-image-lightbox", + "stars": 1, + "downloads": 0, + "install": "dsh plugin --profile web add @loyalchiiina/dsh-chat-image-lightbox", + "added": "2026-08-24" + }, + { + "name": "dsh-workbench-layout", + "owner": "lsq-dsh-plugins", + "url": "https://github.com/lsq-dsh-plugins/dsh-workbench-layout", + "page": "https://awesome-dsh-plugin.com/p/lsq-dsh-plugins/dsh-workbench-layout/", + "category": "ui", + "description": { + "en": "Rearranges DSH Web into a three-column workbench with file editing, Git views, Markdown preview, and workspace terminals.", + "zh": "将 DSH Web 重排为三栏工作台,提供文件编辑、Git 视图、Markdown 预览和工作区终端。" + }, + "npm": "@lsq64737/dsh-workbench-layout", + "stars": 1, + "downloads": 447, + "install": "dsh plugin --profile web add @lsq64737/dsh-workbench-layout", + "added": "2026-08-25", + "screenshots": [ + "https://raw.githubusercontent.com/lsq-dsh-plugins/dsh-workbench-layout/HEAD/assets/workbench-files-and-chat.png", + "https://raw.githubusercontent.com/lsq-dsh-plugins/dsh-workbench-layout/HEAD/assets/workbench-git-diff.png" + ] + }, + { + "name": "dsh-web-attention-badge", + "owner": "Luaphes", + "url": "https://github.com/Luaphes/dsh-web-attention-badge", + "page": "https://awesome-dsh-plugin.com/p/Luaphes/dsh-web-attention-badge/", + "category": "ui", + "description": { + "en": "Attention reminders: frame badge, tab-title count, and a status-colored whale favicon for sessions waiting for input or finished unopened.", + "zh": "会话需要你时三处同时亮起:角标、标签页标题计数、按状态换色的鲸鱼 favicon。" + }, + "npm": "dsh-web-attention-badge", + "stars": 5, + "downloads": 455, + "install": "dsh plugin --profile web add dsh-web-attention-badge", + "added": "2026-08-14" + }, + { + "name": "dsh-quick-toc", + "owner": "LyaxZ", + "url": "https://github.com/LyaxZ/dsh-quick-toc", + "page": "https://awesome-dsh-plugin.com/p/LyaxZ/dsh-quick-toc/", + "category": "ui", + "description": { + "en": "Conversation TOC for DeepSeek Harness: markdown heading outline grouped by turn, with auto-follow highlight and smooth jump navigation.", + "zh": "DeepSeek Harness 对话大纲插件:按回合分组的 Markdown 标题目录,自动跟随高亮,平滑跳转导航。" + }, + "npm": "dsh-quick-toc", + "tarball": "https://github.com/LyaxZ/dsh-quick-toc/releases/latest/download/dsh-quick-toc.tgz", + "stars": 1, + "downloads": 894, + "install": "dsh plugin --profile web add dsh-quick-toc", + "added": "2026-08-18" + }, + { + "name": "dsh-soup", + "owner": "lyhue1991", + "url": "https://github.com/lyhue1991/dsh-soup", + "page": "https://awesome-dsh-plugin.com/p/lyhue1991/dsh-soup/", + "category": "ui", + "description": { + "en": "Project file explorer in the details column (multi-select, drag-move, upload, download, rename, trash, system-open) with an in-session read-only preview tab for Markdown/HTML/JSON/CSV/notebook/PDF + code highlighting, a live model throughput badge, and a multiline GoalBar.", + "zh": "DSH 右侧 details 列项目资源管理器(多选、拖拽移动、上传、下载、重命名、废纸篓、系统打开),会话内只读预览 Markdown/HTML/JSON/CSV/Notebook/PDF 与代码高亮,附带模型实时吞吐徽标与多行 GoalBar。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:lyhue1991/dsh-soup", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/lyhue1991/dsh-soup/HEAD/view.jpg" + ] + }, + { + "name": "dsh-file-review-tab", + "owner": "Lzh3070", + "url": "https://github.com/Lzh3070/dsh-file-review-tab", + "page": "https://awesome-dsh-plugin.com/p/Lzh3070/dsh-file-review-tab/", + "category": "ui", + "description": { + "en": "Review agent file changes as a dsh-better-sidebar sidebar tab or chat turn-tail row — line-level diffs and undo.", + "zh": "把 agent 的文件改动审查放进 dsh-better-sidebar 侧边栏 Tab 或对话尾部行,支持行级 diff 与撤销。" + }, + "npm": "dsh-file-review-tab", + "stars": 11, + "downloads": 596, + "install": "dsh plugin --profile web add dsh-file-review-tab", + "added": "2026-08-20", + "screenshots": [ + "https://raw.githubusercontent.com/Lzh3070/dsh-file-review-tab/HEAD/docs/screenshot.png" + ] + }, + { + "name": "deepseek-harness-zh_pro", + "owner": "magian1127", + "url": "https://github.com/magian1127/deepseek-harness-zh_pro", + "page": "https://awesome-dsh-plugin.com/p/magian1127/deepseek-harness-zh_pro/", + "category": "ui", + "description": { + "en": "UI enhancement suite for the DSH web client: Chinese locale completion, one-row stats, auto-expanded thinking, chat width, session archiving and delete, plus an optional prompt-injection switch (off by default).", + "zh": "DSH 网页 UI 综合增强:中文补全、统计单行显示、思考自动展开、对话宽度、会话归档与删除,并可选的提示词注入(默认关闭)。" + }, + "npm": "deepseek-harness-zh_pro", + "stars": 22, + "downloads": 3852, + "install": "dsh plugin --profile web add deepseek-harness-zh_pro", + "added": "2026-08-15" + }, + { + "name": "dsh-autoload-history", + "owner": "magicOF2", + "url": "https://github.com/magicOF2/dsh-autoload-history", + "page": "https://awesome-dsh-plugin.com/p/magicOF2/dsh-autoload-history/", + "category": "ui", + "description": { + "en": "Automatically loads the full conversation history when a session opens, so older messages are ready without clicking \"Load earlier\".", + "zh": "打开会话时自动加载全部历史消息,免去反复点击「加载更早」。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:magicOF2/dsh-autoload-history", + "added": "2026-08-19" + }, + { + "name": "dsh-chat-width-customizer", + "owner": "magicOF2", + "url": "https://github.com/magicOF2/dsh-chat-width-customizer", + "page": "https://awesome-dsh-plugin.com/p/magicOF2/dsh-chat-width-customizer/", + "category": "ui", + "description": { + "en": "Session-header button that cycles the conversation width (748-1600px), widening the chat column, composer, and user bubbles together.", + "zh": "会话标题栏按钮循环切换对话宽度(748–1600px),消息区、输入框、用户气泡同步加宽。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:magicOF2/dsh-chat-width-customizer", + "added": "2026-08-15" + }, + { + "name": "dsh-schedule", + "owner": "magicOF2", + "url": "https://github.com/magicOF2/dsh-schedule", + "page": "https://awesome-dsh-plugin.com/p/magicOF2/dsh-schedule/", + "category": "ui", + "description": { + "en": "Local long-term schedule panel: top-bar \"日程\" text button, today/week/history month-calendar views with stats, completion checkmarks with per-day history, schedule↔conversation links with click-to-jump, recurring tasks (daily/weekly), optional auto carry-over for unfinished one-time tasks, and dailytask_* agent tools.", + "zh": "本地长期日程面板:顶栏「日程」文字按钮、今天/本周/历史月历统计视图、完成打勾(按天记录历史)、日程↔会话链接点击跳转、重复日程(每天/每周)、一次性日程可选未完成自动顺延,以及 dailytask_* agent 工具。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:magicOF2/dsh-schedule", + "added": "2026-08-26" + }, + { + "name": "dsh-turn-marks", + "owner": "magicOF2", + "url": "https://github.com/magicOF2/dsh-turn-marks", + "page": "https://awesome-dsh-plugin.com/p/magicOF2/dsh-turn-marks/", + "category": "ui", + "description": { + "en": "Left-edge turn-marks strip in chat: one bar per user message, click to jump to that message, hover for a preview, active bar turns white.", + "zh": "会话左侧消息标记条:每发一条消息多一根条条,点击跳转到该消息、悬停预览内容,当前消息对应的条条变白。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:magicOF2/dsh-turn-marks", + "added": "2026-08-15" + }, + { + "name": "dsh-chat-rail", + "owner": "Max-Null", + "url": "https://github.com/Max-Null/dsh-chat-rail", + "page": "https://awesome-dsh-plugin.com/p/Max-Null/dsh-chat-rail/", + "category": "ui", + "description": { + "en": "Scroll-painting message rail for DeepSeek Harness: a slim right-edge rail with one indicator per user message, scroll-spy follows the reading position, hover unfolds a canvas with full message previews, and clicking jumps to any history message; animates in sync with better-sidebar panels.", + "zh": "DeepSeek Harness 画卷式消息导航栏:会话面板右侧一条竖排导轨,每条用户消息一个指示点,scroll-spy 跟随阅读位置,hover 展开画卷查看完整消息预览,点击跳转任意历史消息;与 better-sidebar 面板动画同步避让。" + }, + "npm": "@max-null/dsh-chat-rail", + "stars": 2, + "downloads": 771, + "install": "dsh plugin --profile web add @max-null/dsh-chat-rail", + "added": "2026-08-21" + }, + { + "name": "dsh-node-appearance", + "owner": "Max-Null", + "url": "https://github.com/Max-Null/dsh-node-appearance", + "page": "https://awesome-dsh-plugin.com/p/Max-Null/dsh-node-appearance/", + "category": "ui", + "description": { + "en": "Conversation node appearance for the DeepSeek Harness web GUI: configurable per-category and per-tool colors (left accent bar + tinted background), plus a show-thinking toggle; pure frontend rendering, takes effect instantly and persists to the DSH user settings file.", + "zh": "DeepSeek Harness Web GUI 会话节点外观插件:按节点类别/工具名可配置着色(左侧色条 + 淡色底),附带\"显示思考过程\"开关;纯前端渲染增强,改动即时生效并持久化到 DSH 用户设置文件。" + }, + "npm": "@max-null/dsh-node-appearance", + "stars": 2, + "downloads": 920, + "install": "dsh plugin --profile web add @max-null/dsh-node-appearance", + "added": "2026-08-21" + }, + { + "name": "dsh-desktop-kit", + "owner": "MDR-EX1000", + "url": "https://github.com/MDR-EX1000/dsh-desktop-kit", + "page": "https://awesome-dsh-plugin.com/p/MDR-EX1000/dsh-desktop-kit/", + "category": "ui", + "description": { + "en": "macOS desktop shell for the DSH web UI: a plugin plus a small Tauri/WKWebView window over the loopback web surface — real macOS fullscreen, single instance, external links delegated to the system browser, and browser-style zoom.", + "zh": "把 DSH Web UI 装进原生 macOS 桌面窗口:插件 + 轻量 Tauri/WKWebView 外壳跑在同一个 loopback 地址上——真 macOS 全屏、单实例、外链交给系统浏览器、类浏览器页面缩放。" + }, + "npm": null, + "tarball": "https://github.com/MDR-EX1000/dsh-desktop-kit/releases/latest/download/dsh-desktop-kit.tgz", + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/MDR-EX1000/dsh-desktop-kit/releases/latest/download/dsh-desktop-kit.tgz\"", + "added": "2026-08-21" + }, + { + "name": "DSH-Claude-Style-Reasoning-Slider", + "owner": "MEMZ-JZY", + "url": "https://github.com/MEMZ-JZY/DSH-Claude-Style-Reasoning-Slider", + "page": "https://awesome-dsh-plugin.com/p/MEMZ-JZY/DSH-Claude-Style-Reasoning-Slider/", + "category": "ui", + "description": { + "en": "Animated Claude-style reasoning-effort slider and model picker that replaces the native DSH model selector.", + "zh": "Claude 风格动画推理等级滑块与模型选择器,替换原生模型选择器。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:MEMZ-JZY/DSH-Claude-Style-Reasoning-Slider", + "added": "2026-08-15" + }, + { + "name": "dsh-codex-pet", + "owner": "mengyun233", + "url": "https://github.com/mengyun233/dsh-codex-pet", + "page": "https://awesome-dsh-plugin.com/p/mengyun233/dsh-codex-pet/", + "category": "ui", + "description": { + "en": "Auto-migrate Codex desktop-pet skins into DSH: an animated corner pet that mirrors agent state (thinking, tool use, awaiting approval, failed, done), with per-session frosted-glass dialogs and a full settings panel.", + "zh": "把 Codex 桌宠皮肤自动迁移到 DSH:右下角动画桌宠随 agent 状态实时变化(思考/工具/等待批准/出错/完成),多会话毛玻璃对话框 + 完整设置面板。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:mengyun233/dsh-codex-pet", + "added": "2026-08-15" + }, + { + "name": "dsh-sticky-note", + "owner": "Meredith2328", + "url": "https://github.com/Meredith2328/dsh-sticky-note", + "page": "https://awesome-dsh-plugin.com/p/Meredith2328/dsh-sticky-note/", + "category": "ui", + "description": { + "en": "Quick sticky notes on the composer toolbar: jot ideas or TODOs, auto-saved as Markdown, one click to send into the chat.", + "zh": "编辑框工具栏便签,随手记点子和 TODO,自动保存为 Markdown,一键发送到对话。" + }, + "npm": null, + "stars": 13, + "downloads": null, + "install": "dsh plugin --profile web add github:Meredith2328/dsh-sticky-note", + "added": "2026-08-14" + }, + { + "name": "dsh-plugin-collapsible-steps", + "owner": "mervyn-teo", + "url": "https://github.com/mervyn-teo/dsh-plugin-collapsible-steps", + "page": "https://awesome-dsh-plugin.com/p/mervyn-teo/dsh-plugin-collapsible-steps/", + "category": "ui", + "description": { + "en": "Collapses consecutive tool-use and thinking steps between messages into a single bracketed, expandable item.", + "zh": "把消息之间连续的工具调用与思考步骤折叠成单个可展开的括号条目。" + }, + "npm": "dsh-plugin-collapsible-steps", + "stars": 1, + "downloads": 186, + "install": "dsh plugin --profile web add dsh-plugin-collapsible-steps", + "added": "2026-08-16" + }, + { + "name": "DeepSeek-Balance-Whale-Widget", + "owner": "MeteorNOX", + "url": "https://github.com/MeteorNOX/DeepSeek-Balance-Whale-Widget", + "page": "https://awesome-dsh-plugin.com/p/MeteorNOX/DeepSeek-Balance-Whale-Widget/", + "category": "ui", + "description": { + "en": "A fixed-corner whale widget showing DeepSeek balance, today usage, per-turn cost and random talk lines, with sound effects and a menu.", + "zh": "右下角常驻的小鲸鱼余额挂件:显示余额、今日已用、每轮对话消耗与随机台词,带音效与设置菜单。" + }, + "npm": "dsh-whale-widget", + "stars": 1007, + "downloads": 5789, + "install": "dsh plugin --profile web add dsh-whale-widget", + "added": "2026-08-23" + }, + { + "name": "dsh-codex-ui", + "owner": "MichengAI", + "url": "https://github.com/MichengAI/dsh-codex-ui", + "page": "https://awesome-dsh-plugin.com/p/MichengAI/dsh-codex-ui/", + "category": "ui", + "description": { + "en": "Rebuilds the DeepSeek Harness web sidebar, workspace tree, search, and turn navigation in a Codex-style layout.", + "zh": "为 DeepSeek Harness 网页端重构 Codex 风格侧栏、工作区会话树、全局搜索和轮次导航。" + }, + "npm": "@michengai/dsh-codex-ui", + "stars": 35, + "downloads": 6233, + "install": "dsh plugin --profile web add @michengai/dsh-codex-ui", + "added": "2026-08-19" + }, + { + "name": "dsh-restart-fab", + "owner": "miisaka19800", + "url": "https://github.com/miisaka19800/dsh-restart-fab", + "page": "https://awesome-dsh-plugin.com/p/miisaka19800/dsh-restart-fab/", + "category": "ui", + "description": { + "en": "Floating restart button for the DSH web GUI: restarts the dsh process in place with a full-screen overlay and auto-reloads the page; no launcher script required, works on Windows/macOS/Linux.", + "zh": "DSH 网页界面右下角的一键重启按钮:全屏遮罩原地重启 dsh 进程并自动刷新页面;无需启动脚本,跨 Windows/macOS/Linux。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:miisaka19800/dsh-restart-fab", + "added": "2026-08-15" + }, + { + "name": "dsh-i18n", + "owner": "mimateinn", + "url": "https://github.com/mimateinn/dsh-i18n", + "page": "https://awesome-dsh-plugin.com/p/mimateinn/dsh-i18n/", + "category": "ui", + "description": { + "en": "Adds 20 locales (Traditional Chinese HK/TW, Japanese, Korean, French, German, Spanish, Portuguese, Italian, Russian, Ukrainian, Polish, Dutch, Turkish, Arabic, Hindi, Indonesian, Vietnamese, Thai, Swedish) to the DeepSeek Harness Web UI, with hand-polished translations, runtime fallbacks, and automatic translation of market and third-party text.", + "zh": "为 DeepSeek Harness 界面新增 20 种语言(繁体中文香港/台湾、日语、韩语、法语、德语、西班牙语、葡萄牙语、意大利语、俄语、乌克兰语、波兰语、荷兰语、土耳其语、阿拉伯语、印地语、印尼语、越南语、泰语、瑞典语),逐条精译并带运行时兜底,另加自动翻译,把市场与第三方文本翻译成你的语言。" + }, + "npm": "@mimateinn/dsh-i18n", + "stars": 1, + "downloads": 259, + "install": "dsh plugin --profile web add @mimateinn/dsh-i18n", + "added": "2026-08-25" + }, + { + "name": "dsh-toolfold", + "owner": "Minecraftbe", + "url": "https://github.com/Minecraftbe/dsh-toolfold", + "page": "https://awesome-dsh-plugin.com/p/Minecraftbe/dsh-toolfold/", + "category": "ui", + "description": { + "en": "Codex-style tool call folding, collapsing multiple tool calls into a single line.", + "zh": "Codex 风格的工具调用折叠,将多个工具调用折叠到一行内。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:Minecraftbe/dsh-toolfold", + "added": "2026-08-15" + }, + { + "name": "dsh-subagent-ui", + "owner": "miuzel", + "url": "https://github.com/miuzel/dsh-subagent-ui", + "page": "https://awesome-dsh-plugin.com/p/miuzel/dsh-subagent-ui/", + "category": "ui", + "description": { + "en": "Search, monitor, interrupt, and batch-archive workspace subagents, featuring real-time activity streaming and an active-agent floating panel.", + "zh": "搜索、监控、打断与批量归档工作区子代理,支持实时活动/思考流式展示与活跃浮窗。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:miuzel/dsh-subagent-ui", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/miuzel/dsh-subagent-ui/HEAD/docs/images/screenshot-1.png", + "https://raw.githubusercontent.com/miuzel/dsh-subagent-ui/HEAD/docs/images/screenshot-2.png" + ] + }, + { + "name": "dsh-chat-index-rail", + "owner": "Mobai-read", + "url": "https://github.com/Mobai-read/dsh-chat-index-rail", + "page": "https://awesome-dsh-plugin.com/p/Mobai-read/dsh-chat-index-rail/", + "category": "ui", + "description": { + "en": "Client-only right-edge chat input index rail: one bar per user message, two-stage hover preview (15 chars, expandable to 40), click-to-jump, and scroll-follow highlight; static npm bundle with no host half.", + "zh": "纯客户端对话输入目录条:每条用户消息一根横条,两级悬停预览(15 字,移入气泡展开 40 字)、点击跳转、滚动跟随高亮;静态 npm 包,无宿主半。" + }, + "npm": "dsh-chat-index-rail", + "stars": 1, + "downloads": 353, + "install": "dsh plugin --profile web add dsh-chat-index-rail", + "added": "2026-08-15" + }, + { + "name": "dsh-subagent-monitor", + "owner": "Mombrane", + "url": "https://github.com/Mombrane/dsh-subagent-monitor", + "page": "https://awesome-dsh-plugin.com/p/Mombrane/dsh-subagent-monitor/", + "category": "ui", + "description": { + "en": "Live subagent run monitor for the Web UI: a sidebar footer trigger plus a fixed top-right card panel showing each subagent of the current session in real time (running/elapsed, terminal outcomes, tree indent), with one-click jump into the child conversation and a return button, refresh-surviving and mobile-hidden by default.", + "zh": "Web UI 子代理实时监视面板:侧边栏底部入口 + 右上角常驻卡片面板,实时展示当前会话每个子代理的运行状态(运行中/耗时/终态)、树形缩进,一键跳转子代理会话并支持返回主会话,刷新自恢复、移动端默认隐藏。" + }, + "npm": "@leetoners/dsh-ui-subagent-monitor", + "stars": 18, + "downloads": 555, + "install": "dsh plugin --profile web add @leetoners/dsh-ui-subagent-monitor", + "added": "2026-08-15" + }, + { + "name": "widget-dock", + "owner": "MorGogh", + "url": "https://github.com/MorGogh/widget-dock", + "page": "https://awesome-dsh-plugin.com/p/MorGogh/widget-dock/", + "category": "ui", + "description": { + "en": "Draggable grid-aligned workbench of 28 mini-cards in the margins of the conversation (API balance, token usage, cost, context pressure, todos, goal, usage heatmap, GitHub repo, image relay), with S/M/L/XL size tiers.", + "zh": "对话两侧空白区的可拖动卡片工作台,28 个小组件(余额、Token 用量、成本、上下文压力、待办、目标、用量热力图、GitHub 仓库、图片中转等),支持 S/M/L/XL 四档尺寸。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:MorGogh/widget-dock", + "added": "2026-08-14" + }, + { + "name": "dsh-visualize", + "owner": "Nagi-ovo", + "url": "https://github.com/Nagi-ovo/dsh-visualize", + "page": "https://awesome-dsh-plugin.com/p/Nagi-ovo/dsh-visualize/", + "category": "ui", + "description": { + "en": "In-conversation generative UI: the model renders interactive HTML cards into the chat stream, with streaming preview and sandboxed rendering.", + "zh": "对话内生成式 UI:模型把交互式 HTML 卡片直接画进会话流,带流式预览与沙箱渲染。" + }, + "npm": null, + "stars": 216, + "downloads": null, + "install": "dsh plugin --profile web add github:Nagi-ovo/dsh-visualize", + "added": "2026-08-13" + }, + { + "name": "dsh-custom-ui", + "owner": "nailing10086-zx", + "url": "https://github.com/nailing10086-zx/dsh-custom-ui", + "page": "https://awesome-dsh-plugin.com/p/nailing10086-zx/dsh-custom-ui/", + "category": "ui", + "description": { + "en": "Right-side file-tree sidebar that opens workspace files, plus a DeepSeek account balance meter in the composer stats line.", + "zh": "右侧文件树侧栏,可打开工作区文件;并在输入框状态行显示 DeepSeek 账户余额。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:nailing10086-zx/dsh-custom-ui", + "added": "2026-08-17" + }, + { + "name": "dsh-show-media", + "owner": "NecromanAlbert", + "url": "https://github.com/NecromanAlbert/dsh-show-media", + "page": "https://awesome-dsh-plugin.com/p/NecromanAlbert/dsh-show-media/", + "category": "ui", + "description": { + "en": "Shows a local image or short video in the current conversation card, with click-to-preview.", + "zh": "在当前对话卡片里展示本地图片或短视频,点击可预览。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:NecromanAlbert/dsh-show-media", + "added": "2026-08-27" + }, + { + "name": "dsh-archive-manager#plugin", + "owner": "Neumannzc", + "url": "https://github.com/Neumannzc/dsh-archive-manager/tree/main/plugin", + "page": "https://awesome-dsh-plugin.com/p/Neumannzc/dsh-archive-manager--plugin/", + "category": "ui", + "description": { + "en": "Beautified archive-manager settings section listing archived sessions grouped by workspace, with a hover unarchive action and relative timestamps.", + "zh": "美化版归档管理设置页:按工作区分组展示归档会话,悬停即可取消归档,附相对时间显示。" + }, + "npm": "@tangzai/dsh-ui-archive-manager", + "stars": 1, + "downloads": 275, + "install": "dsh plugin --profile web add @tangzai/dsh-ui-archive-manager", + "added": "2026-08-19" + }, + { + "name": "dsh-vision-plugin", + "owner": "nexsjournal", + "url": "https://github.com/nexsjournal/dsh-vision-plugin", + "page": "https://awesome-dsh-plugin.com/p/nexsjournal/dsh-vision-plugin/", + "category": "ui", + "description": { + "en": "Installs an image-input checkbox into the Models catalog so a custom model can declare image input and receive images directly; includes an optional BYO vision relay.", + "zh": "给模型目录装上「图片输入」勾选框,自定义模型声明后可直接收图;附带可选的 BYO 视觉中继。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:nexsjournal/dsh-vision-plugin", + "added": "2026-08-17" + }, + { + "name": "dsh-open-in-x", + "owner": "nikoart-liu", + "url": "https://github.com/nikoart-liu/dsh-open-in-x", + "page": "https://awesome-dsh-plugin.com/p/nikoart-liu/dsh-open-in-x/", + "category": "ui", + "description": { + "en": "Open DSH workspace directories in installed file managers, terminals, and supported editors from the Web UI.", + "zh": "从 DSH Web UI 使用已安装的文件管理器、终端和受支持的编辑器打开工作区目录。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:nikoart-liu/dsh-open-in-x", + "added": "2026-08-27" + }, + { + "name": "dsh-keyboard-history", + "owner": "NormanFxxkingRockwell", + "url": "https://github.com/NormanFxxkingRockwell/dsh-keyboard-history", + "page": "https://awesome-dsh-plugin.com/p/NormanFxxkingRockwell/dsh-keyboard-history/", + "category": "ui", + "description": { + "en": "Minimal ↑/↓ input history for the DSH web composer: press ↑/↓ to walk back through sent messages. Nothing else.", + "zh": "DSH 会话输入框的极简输入历史:按 ↑/↓ 翻阅发过的消息。只有方向键,别无其他。" + }, + "npm": "dsh-keyboard-history", + "stars": 2, + "downloads": 545, + "install": "dsh plugin --profile web add dsh-keyboard-history", + "added": "2026-08-19" + }, + { + "name": "folder-tree-sh", + "owner": "Nothree-code", + "url": "https://github.com/Nothree-code/folder-tree-sh", + "page": "https://awesome-dsh-plugin.com/p/Nothree-code/folder-tree-sh/", + "category": "ui", + "description": { + "en": "Workspace file tree for the Web UI: multi-tab preview (text/DOCX/PDF/Markdown/CSV/images), inline Markdown editing, a Git changes panel, and file operations (rename/delete/copy/move/create) via context menu.", + "zh": "工作区文件树插件:多标签预览(文本/DOCX/PDF/Markdown/CSV/图片)、Markdown 内联编辑、Git 变更面板与右键文件操作(重命名/删除/复制/移动/新建)。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:Nothree-code/folder-tree-sh", + "added": "2026-08-15" + }, + { + "name": "dsh-mobile-webui", + "owner": "Odefined", + "url": "https://github.com/Odefined/dsh-mobile-webui", + "page": "https://awesome-dsh-plugin.com/p/Odefined/dsh-mobile-webui/", + "category": "ui", + "description": { + "en": "Mobile-viewport fixes for the dsh web GUI: overlay drawer sidebar with swipe gestures, icon-only composer with a mode · model · effort status line, centered popovers, and horizontal-overflow/scrollbar centering fixes.", + "zh": "修复 dsh web GUI 的手机视口体验:抽屉式侧栏与滑动手势、纯图标 composer 与「权限·模型·思考深度」状态行、浮窗居中、横向溢出与滚动条挤压居中修复。" + }, + "npm": "dsh-mobile-webui", + "stars": 1, + "downloads": 916, + "install": "dsh plugin --profile web add dsh-mobile-webui", + "added": "2026-08-20" + }, + { + "name": "dsh-annotation", + "owner": "omdsh-dev", + "url": "https://github.com/omdsh-dev/dsh-annotation", + "page": "https://awesome-dsh-plugin.com/p/omdsh-dev/dsh-annotation/", + "category": "ui", + "description": { + "en": "Select text → annotate → send with your message; replies map back to each annotation.", + "zh": "选中文字→批注→随消息发送,回复按批注逐条对照。" + }, + "npm": "@changfenhuang/dsh-annotation", + "stars": 99, + "downloads": 295, + "install": "dsh plugin --profile web add @changfenhuang/dsh-annotation", + "added": "2026-08-13", + "screenshots": [ + "https://github.com/user-attachments/assets/c3186efc-44d3-4e7f-9523-1902d9d037e9", + "https://github.com/user-attachments/assets/0b48ac02-4648-4b94-8d8f-344f8b7c25b4", + "https://github.com/user-attachments/assets/8b2610d0-3d00-41be-b314-bac2fe616787", + "https://github.com/user-attachments/assets/9b66deea-3786-4296-9b0d-52873a15f5e1" + ] + }, + { + "name": "dsh-at-file", + "owner": "omdsh-dev", + "url": "https://github.com/omdsh-dev/dsh-at-file", + "page": "https://awesome-dsh-plugin.com/p/omdsh-dev/dsh-at-file/", + "category": "ui", + "description": { + "en": "Codex-style `@file` mentions: search workspace files in the composer and attach their contents to prompts.", + "zh": "Codex 风格的 `@file` 文件引用,输入框里直接搜索并引用工作区文件。" + }, + "npm": null, + "stars": 474, + "downloads": null, + "install": "dsh plugin --profile web add github:omdsh-dev/dsh-at-file", + "added": "2026-08-13" + }, + { + "name": "DSH-better-sidebar", + "owner": "omdsh-dev", + "url": "https://github.com/omdsh-dev/DSH-better-sidebar", + "page": "https://awesome-dsh-plugin.com/p/omdsh-dev/DSH-better-sidebar/", + "category": "ui", + "description": { + "en": "Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.", + "zh": "侧边栏完整工作台:内置文件渲染编辑、终端、Git 与子代理,支持三方插件注册新 Tab。" + }, + "npm": "dsh-better-sidebar", + "stars": 2918, + "downloads": 145679, + "install": "dsh plugin --profile web add dsh-better-sidebar", + "added": "2026-08-14" + }, + { + "name": "dsh-genui", + "owner": "omdsh-dev", + "url": "https://github.com/omdsh-dev/dsh-genui", + "page": "https://awesome-dsh-plugin.com/p/omdsh-dev/dsh-genui/", + "category": "ui", + "description": { + "en": "Interactive UI components rendered inline in replies: layout, charts, forms, quizzes, mermaid, 3D scenes, and an action event loop back to the model.", + "zh": "助手回复内渲染交互式 UI 组件:布局、图表、表单、测验、mermaid、3D 场景与回传事件循环。" + }, + "npm": "@changfenhuang/dsh-genui", + "stars": 336, + "downloads": 748, + "install": "dsh plugin --profile web add @changfenhuang/dsh-genui", + "added": "2026-08-13", + "screenshots": [ + "https://raw.githubusercontent.com/omdsh-dev/dsh-genui/main/assets/showcase-panel.png", + "https://raw.githubusercontent.com/omdsh-dev/dsh-genui/main/assets/showcase-plot.png", + "https://raw.githubusercontent.com/omdsh-dev/dsh-genui/main/assets/showcase.png", + "https://raw.githubusercontent.com/omdsh-dev/dsh-genui/main/assets/demo-thumb.png" + ] + }, + { + "name": "ex-setting", + "owner": "omdsh-dev", + "url": "https://github.com/omdsh-dev/ex-setting", + "page": "https://awesome-dsh-plugin.com/p/omdsh-dev/ex-setting/", + "category": "ui", + "description": { + "en": "Settings extensions for DSH.", + "zh": "DSH 的设置扩展。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:omdsh-dev/ex-setting", + "added": "2026-08-13" + }, + { + "name": "web-components", + "owner": "omdsh-dev", + "url": "https://github.com/omdsh-dev/web-components", + "page": "https://awesome-dsh-plugin.com/p/omdsh-dev/web-components/", + "category": "ui", + "description": { + "en": "Web Components support.", + "zh": "Web Components 支持。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:omdsh-dev/web-components", + "added": "2026-08-13" + }, + { + "name": "dsh-agfs", + "owner": "openAGFS", + "url": "https://github.com/openAGFS/dsh-agfs", + "page": "https://awesome-dsh-plugin.com/p/openAGFS/dsh-agfs/", + "category": "ui", + "description": { + "en": "File-browser web app for DSH: React frontend and REST API served by the host webserver, a /dsh-agfs command that opens at the current session workspace, and a browse_files model tool.", + "zh": "文件浏览器 Web 应用:React 前端与 REST API 由宿主 webserver 托管,/dsh-agfs 命令自动定位当前工作区,附 browse_files 模型工具。" + }, + "npm": "@open-agfs/dsh-agfs", + "stars": 3, + "downloads": 2052, + "install": "dsh plugin --profile web add @open-agfs/dsh-agfs", + "added": "2026-08-15" + }, + { + "name": "opencues#integrations-dsh", + "owner": "opencues", + "url": "https://github.com/opencues/opencues/tree/master/integrations/dsh", + "page": "https://awesome-dsh-plugin.com/p/opencues/opencues--integrations-dsh/", + "category": "ui", + "description": { + "en": "Word alternatives and underscore-gated fill-ins in the composer. End a line with _ and it is filled; misspellings are flagged as you type. Routes through the model dsh is already configured with, so it needs no API key.", + "zh": "在输入框内提供同义词替换与以下划线触发的补全:一行以 _ 结尾即自动补全,拼写错误在输入时即时标出。它走 dsh 已配置好的模型,因此无需额外的 API key。" + }, + "npm": "@opencues/dsh", + "stars": 37, + "downloads": 351, + "install": "dsh plugin --profile web add @opencues/dsh", + "added": "2026-08-18" + }, + { + "name": "Martty#npm", + "owner": "openma-ai", + "url": "https://github.com/openma-ai/Martty/tree/main/npm", + "page": "https://awesome-dsh-plugin.com/p/openma-ai/Martty--npm/", + "category": "ui", + "description": { + "en": "A DSH-first Rust/ratatui agent TUI with streamed tool calls, subagents, durable sessions, and a Cordis-extensible client UI.", + "zh": "面向 DeepSeek Harness 的 Rust/ratatui Agent TUI,支持流式工具调用、子代理、持久会话和可扩展的 Cordis 客户端界面。" + }, + "npm": "@openma/deepseek-harness-tui", + "stars": 60, + "downloads": 4328, + "install": "dsh plugin --profile web add @openma/deepseek-harness-tui", + "added": "2026-08-23" + }, + { + "name": "dsh-paste-names", + "owner": "PaoMoXML", + "url": "https://github.com/PaoMoXML/dsh-paste-names", + "page": "https://awesome-dsh-plugin.com/p/PaoMoXML/dsh-paste-names/", + "category": "ui", + "description": { + "en": "Paste non-image files or folders into the chat composer as native @path references, or drag them in as absolute paths, instead of the images-only error.", + "zh": "将非图片文件或文件夹以原生 @path 引用粘贴进聊天输入框,或拖放插入绝对路径,替代仅支持图片的报错。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:PaoMoXML/dsh-paste-names", + "added": "2026-08-24" + }, + { + "name": "dsh-plugin-workbench", + "owner": "Pasumao", + "url": "https://github.com/Pasumao/dsh-plugin-workbench", + "page": "https://awesome-dsh-plugin.com/p/Pasumao/dsh-plugin-workbench/", + "category": "ui", + "description": { + "en": "Adds a VS Code-style workspace file explorer to the dsh web GUI: file tree, editable preview with syntax highlighting, tabs, line numbers, right-click file operations, and inline image preview.", + "zh": "为 dsh 网页添加 VS Code 风格工作区文件浏览器:文件树、可编辑预览(语法高亮、标签页、行号)、右键文件操作与图片内联预览。" + }, + "npm": "dsh-plugin-workbench", + "stars": 3, + "downloads": 2026, + "install": "dsh plugin --profile web add dsh-plugin-workbench", + "added": "2026-08-21" + }, + { + "name": "dsh-side-monitor", + "owner": "pc439527", + "url": "https://github.com/pc439527/dsh-side-monitor", + "page": "https://awesome-dsh-plugin.com/p/pc439527/dsh-side-monitor/", + "category": "ui", + "description": { + "en": "Sidebar system monitor for the DSH Web UI: live host overview (CPU/memory/network/disk), process list, and Docker container status, all read-only.", + "zh": "侧边栏系统监控:实时查看宿主机概览(CPU/内存/网络/磁盘)、进程列表与 Docker 容器状态,全程只读。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:pc439527/dsh-side-monitor", + "added": "2026-08-16" + }, + { + "name": "dsh-pathlink", + "owner": "penguin-oo", + "url": "https://github.com/penguin-oo/dsh-pathlink", + "page": "https://awesome-dsh-plugin.com/p/penguin-oo/dsh-pathlink/", + "category": "ui", + "description": { + "en": "Ctrl+click file paths and links in chat: paths open their folder in the OS file manager (with the file selected), links open in a new browser tab.", + "zh": "在对话中 Ctrl+点击文件路径与链接:路径在文件管理器中定位所在文件夹,链接在新标签页打开。" + }, + "npm": "dsh-pathlink", + "stars": 2, + "downloads": 803, + "install": "dsh plugin --profile web add dsh-pathlink", + "added": "2026-08-15" + }, + { + "name": "deepseek-harness-genui", + "owner": "pengyue-polaron", + "url": "https://github.com/pengyue-polaron/deepseek-harness-genui", + "page": "https://awesome-dsh-plugin.com/p/pengyue-polaron/deepseek-harness-genui/", + "category": "ui", + "description": { + "en": "Code-first React and TypeScript task apps rendered inline, in Canvas, full screen, or on localhost, with interaction state available to later agent turns and approval-gated MCP and API access.", + "zh": "Code-first React + TypeScript 任务应用,支持 Inline、Canvas、全屏与 localhost;交互状态可供 Agent 后续轮次读取,MCP 和 API 访问在用户授权后执行。" + }, + "npm": "dsh-plugin-genui", + "stars": 106, + "downloads": 2216, + "install": "dsh plugin --profile web add dsh-plugin-genui", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/pengyue-polaron/deepseek-harness-genui/HEAD/screenshots/en/code-path-canvas.jpg", + "https://raw.githubusercontent.com/pengyue-polaron/deepseek-harness-genui/HEAD/screenshots/en/code-path-inline.jpg", + "https://raw.githubusercontent.com/pengyue-polaron/deepseek-harness-genui/HEAD/screenshots/en/photosynthesis-explorer.jpg", + "https://raw.githubusercontent.com/pengyue-polaron/deepseek-harness-genui/HEAD/screenshots/en/calendar-planner.jpg", + "https://raw.githubusercontent.com/pengyue-polaron/deepseek-harness-genui/HEAD/screenshots/en/code-path-explorer.jpg" + ] + }, + { + "name": "dsh-composer-history", + "owner": "PerryLink", + "url": "https://github.com/PerryLink/dsh-composer-history", + "page": "https://awesome-dsh-plugin.com/p/PerryLink/dsh-composer-history/", + "category": "ui", + "description": { + "en": "Terminal-style input history for the web composer: edge-first arrow-key recall with exact draft/caret restore, browser-local persisted history, Ctrl+R reverse search, and sliding-context awareness; 0.5.0 adds a smart input layer — cross-session snippets (/save, /load), prompt templates with variables, reuse insights, and compaction-summary highlighting.", + "zh": "Web 作曲器终端风格输入历史:边缘优先的方向键召回并精确还原草稿与光标、浏览器本地持久化历史、Ctrl+R 反向搜索,以及滑动上下文感知;0.5.0 再加智能输入层——跨会话片段库(/save、/load)、带变量的提示词模板、复用洞察与压缩摘要高亮。" + }, + "npm": "dsh-composer-history", + "stars": 7, + "downloads": 1554, + "install": "dsh plugin --profile web add dsh-composer-history", + "added": "2026-08-15" + }, + { + "name": "dsh-edex-jarvis-ui#bundle", + "owner": "ph4310822", + "url": "https://github.com/ph4310822/dsh-edex-jarvis-ui/tree/main/packages/bundle", + "page": "https://awesome-dsh-plugin.com/p/ph4310822/dsh-edex-jarvis-ui--packages-bundle/", + "category": "ui", + "description": { + "en": "JARVIS-style sci-fi HUD shell over the DSH web GUI: electric-cyan VITAL SIGNS / RT-MONITOR / RT-LOG left bar, POWER CORE gauges and a rotating RADAR scan disc on the right with up/down traffic chart, filesystem browser with file preview, and a terminal-styled composer with a JARVIS header strip that follows the active workspace.", + "zh": "在 DSH Web 界面上叠加 JARVIS 风格科幻 HUD 外壳:电光青色 VITAL SIGNS 生命体征 / RT-MONITOR 实时监控 / RT-LOG 事件日志左栏,右侧 POWER CORE 能源核心仪表、旋转扫描的 RADAR 雷达盘与上下行流量图,左下文件系统浏览器(带文件预览),以及带 JARVIS 头部横幅、跟随当前工作区的终端风格输入框。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:ph4310822/dsh-edex-jarvis-ui#path:/packages/bundle", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/ph4310822/dsh-edex-jarvis-ui/HEAD/packages/bundle/assets/screenshot.png", + "https://raw.githubusercontent.com/ph4310822/dsh-edex-jarvis-ui/HEAD/packages/bundle/assets/traffic-region.png" + ] + }, + { + "name": "dsh-meow-smooth", + "owner": "Phant0Meow", + "url": "https://github.com/Phant0Meow/dsh-meow-smooth", + "page": "https://awesome-dsh-plugin.com/p/Phant0Meow/dsh-meow-smooth/", + "category": "ui", + "description": { + "en": "Mobile-first UX polish for the DSH Web UI: composer auto-fold, enter-as-newline on phones, compact sidebar/header/settings, zoom and overscroll lock, plus approval/question/completion notifications via in-page cards, Web Push, and webhook — zero dsh changes.", + "zh": "手机优先的 DSH 前端体验优化:输入框自动折叠、手机回车换行、侧边栏/顶部栏/设置页压缩、禁缩放回弹,以及审批/提问/任务完成通知(页面卡片、Web Push、webhook);零 dsh 本体改动。" + }, + "npm": "meow-smooth", + "stars": 18, + "downloads": 814, + "install": "dsh plugin --profile web add meow-smooth", + "added": "2026-08-22" + }, + { + "name": "dsh-ui-harmonizer", + "owner": "Physicolor", + "url": "https://github.com/Physicolor/dsh-ui-harmonizer", + "page": "https://awesome-dsh-plugin.com/p/Physicolor/dsh-ui-harmonizer/", + "category": "ui", + "description": { + "en": "UI harmonizer for DeepSeek Harness: normalizes unfinished or self-contradictory official UI, reconciles style conflicts between installed plugins, and unifies the visual language via official design tokens — with live-customizable chat width, markdown font size, workspace scale, UI font stack and a rounded center-column card from Settings → General; client-only dsh bundle, zero model cost, no host side, removable without residue.", + "zh": "DeepSeek Harness UI 协调器:规范化官方未完善、自相矛盾的界面,协调已装插件间的样式冲突并统一视觉语言(全部遵循官方设计令牌)——支持实时自定义对话宽度、markdown 字号、工作区缩放、UI 字体栈,并在设置页提供圆角中栏卡片;纯客户端 dsh bundle,零模型开销,无 host 端,可无残留移除。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Physicolor/dsh-ui-harmonizer", + "added": "2026-08-27" + }, + { + "name": "dsh-widgets", + "owner": "Physicolor", + "url": "https://github.com/Physicolor/dsh-widgets", + "page": "https://awesome-dsh-plugin.com/p/Physicolor/dsh-widgets/", + "category": "ui", + "description": { + "en": "Right-hand widget rail for DeepSeek Harness Web UI: live session stats, task/context/heatmap/quote widgets, OpenCode Go quota, multi-column grid layouts (1/2/4 columns) with 2x4 long tiles, a continuous magnification wave, and per-instance sizing; widgets are installable, reorderable and resizable from Settings > Components.", + "zh": "DeepSeek Harness Web UI 右侧部件栏:实时会话统计、任务/上下文/热力图/引用部件、OpenCode Go 用量、多列网格布局(1/2/4 列)与 2x4 长切片、连续放大波浪与逐实例尺寸;部件可在设置页安装、排序与调整大小。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Physicolor/dsh-widgets", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/Physicolor/dsh-widgets/HEAD/docs/screenshots/rail-widgets.png", + "https://raw.githubusercontent.com/Physicolor/dsh-widgets/HEAD/docs/screenshots/dock-magnify.png", + "https://raw.githubusercontent.com/Physicolor/dsh-widgets/HEAD/docs/screenshots/add-panel.png", + "https://raw.githubusercontent.com/Physicolor/dsh-widgets/HEAD/docs/screenshots/widget-cards.png" + ] + }, + { + "name": "dsh-plugins#dsh-desktop-entry", + "owner": "pingg02", + "url": "https://github.com/pingg02/dsh-plugins/tree/main/dsh-desktop-entry", + "page": "https://awesome-dsh-plugin.com/p/pingg02/dsh-plugins--dsh-desktop-entry/", + "category": "ui", + "description": { + "en": "Creates a Windows desktop shortcut that starts DeepSeek Harness when needed and opens the Web UI in a Chrome or Edge app window.", + "zh": "创建 Windows 桌面快捷方式,必要时启动 DeepSeek Harness,并用 Chrome 或 Edge 应用窗口打开 Web UI。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:pingg02/dsh-plugins#path:/dsh-desktop-entry", + "added": "2026-08-21" + }, + { + "name": "dsh-plugins#dsh-quote-selection", + "owner": "pingg02", + "url": "https://github.com/pingg02/dsh-plugins/tree/main/dsh-quote-selection", + "page": "https://awesome-dsh-plugin.com/p/pingg02/dsh-plugins--dsh-quote-selection/", + "category": "ui", + "description": { + "en": "Lets users quote selected conversation text with optional notes, then attaches the references to their next message.", + "zh": "引用选中的对话文本并添加可选备注,在发送下一条消息时自动附带引用内容。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:pingg02/dsh-plugins#path:/dsh-quote-selection", + "added": "2026-08-21" + }, + { + "name": "dsh-raw-html", + "owner": "plolpl789", + "url": "https://github.com/plolpl789/dsh-raw-html", + "page": "https://awesome-dsh-plugin.com/p/plolpl789/dsh-raw-html/", + "category": "ui", + "description": { + "en": "Renders agent HTML output as real UI in the DeepSeek Harness web GUI, with visual cards, KaTeX math, Mermaid diagrams, built-in open-source fonts, and design-spec injection.", + "zh": "让 DeepSeek Harness Web GUI 把 agent 输出的 HTML 渲染成真实界面,支持视觉卡片、KaTeX 公式、Mermaid 图表与内置开源字体,并向 agent 注入设计规范。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:plolpl789/dsh-raw-html", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/plolpl789/dsh-raw-html/HEAD/docs/images/banner-1.jpg", + "https://raw.githubusercontent.com/plolpl789/dsh-raw-html/HEAD/docs/images/banner-2.jpg", + "https://raw.githubusercontent.com/plolpl789/dsh-raw-html/HEAD/docs/images/banner-3.jpg", + "https://raw.githubusercontent.com/plolpl789/dsh-raw-html/HEAD/docs/images/banner-4.jpg", + "https://raw.githubusercontent.com/plolpl789/dsh-raw-html/HEAD/docs/images/banner-5.jpg" + ] + }, + { + "name": "dsh-composer-stretch", + "owner": "Pudge1996", + "url": "https://github.com/Pudge1996/dsh-composer-stretch", + "page": "https://awesome-dsh-plugin.com/p/Pudge1996/dsh-composer-stretch/", + "category": "ui", + "description": { + "en": "Expand button for the DSH Web UI composer — appears at 3+ lines, stretches to near-fullscreen height, auto-collapses on send, and Enter inserts newlines in expanded mode.", + "zh": "输入框全屏扩展按钮——内容达 3 行以上时出现,点击扩展至全屏高度,发送后自动折叠,扩展模式下 Enter 换行。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Pudge1996/dsh-composer-stretch", + "added": "2026-08-28" + }, + { + "name": "dsh-bili-widget", + "owner": "pyf2818", + "url": "https://github.com/pyf2818/dsh-bili-widget", + "page": "https://awesome-dsh-plugin.com/p/pyf2818/dsh-bili-widget/", + "category": "ui", + "description": { + "en": "Floating Bilibili video mini-player for DSH — always-on-top window with discover/search/follow-UP channels, auto-playlist, mini & focus modes, persisted watch history and window minimize-to-ball.", + "zh": "DSH 里的 B站悬浮看片小窗:置顶窗口,含推荐/热门/排行/精选/关注/AI 频道与搜索,自动连播、迷你专注模式、历史持久化与最小化悬浮球。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:pyf2818/dsh-bili-widget", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/pyf2818/dsh-bili-widget/main/assets/main.png", + "https://raw.githubusercontent.com/pyf2818/dsh-bili-widget/main/assets/search.png", + "https://raw.githubusercontent.com/pyf2818/dsh-bili-widget/main/assets/hot.png", + "https://raw.githubusercontent.com/pyf2818/dsh-bili-widget/main/assets/help.png", + "https://raw.githubusercontent.com/pyf2818/dsh-bili-widget/main/assets/mini.png" + ] + }, + { + "name": "dsh-reasoning-slider", + "owner": "qjcnmd", + "url": "https://github.com/qjcnmd/dsh-reasoning-slider", + "page": "https://awesome-dsh-plugin.com/p/qjcnmd/dsh-reasoning-slider/", + "category": "ui", + "description": { + "en": "Codex-style reasoning-effort slider embedded in the model selector.", + "zh": "Codex 风格推理等级滑块,内嵌于模型选择器,拖动切换推理档位。" + }, + "npm": "reasoning-slider", + "stars": 6, + "downloads": 848, + "install": "dsh plugin --profile web add reasoning-slider", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/qjcnmd/dsh-reasoning-slider/main/assets/screenshots/deepseek-v4-pro-max.png", + "https://raw.githubusercontent.com/qjcnmd/dsh-reasoning-slider/main/assets/screenshots/deepseek-v4-pro-off.png", + "https://raw.githubusercontent.com/qjcnmd/dsh-reasoning-slider/main/assets/screenshots/kimi-k3-max.png", + "https://raw.githubusercontent.com/qjcnmd/dsh-reasoning-slider/main/assets/screenshots/minimax-m3-minimal.png" + ] + }, + { + "name": "dsh-guise", + "owner": "Quinn2006", + "url": "https://github.com/Quinn2006/dsh-guise", + "page": "https://awesome-dsh-plugin.com/p/Quinn2006/dsh-guise/", + "category": "ui", + "description": { + "en": "Persona wardrobe for DeepSeek Harness: save multiple personas, apply them globally or per workspace, with a master switch, an adjustable API balance guard, and a lazy token-loving tired mode that speaks on the agent's behalf when quota runs out. Files-as-config, hot reload, zero dependencies.", + "zh": "给 DSH 的 agent 穿上主人钦定的人设:多套人格随心换,全局或按工作区生效,内置人设库、总开关、余额预警与没电模式(80 句随机话术)。文件即配置、保存即生效、零依赖。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Quinn2006/dsh-guise", + "added": "2026-08-19" + }, + { + "name": "dsh-deeplink", + "owner": "qyw233", + "url": "https://github.com/qyw233/dsh-deeplink", + "page": "https://awesome-dsh-plugin.com/p/qyw233/dsh-deeplink/", + "category": "ui", + "description": { + "en": "Deep links: open a specific session or workspace via `?session=` / `?workspace=`.", + "zh": "`?session=` / `?workspace=` 深链直达指定项目对话。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:qyw233/dsh-deeplink", + "added": "2026-08-13" + }, + { + "name": "dsh-ui-translate", + "owner": "RadicalGitter", + "url": "https://github.com/RadicalGitter/dsh-ui-translate", + "page": "https://awesome-dsh-plugin.com/p/RadicalGitter/dsh-ui-translate/", + "category": "ui", + "description": { + "en": "Translates visible Chinese UI and content in DeepSeek Harness Web, using a browser-local OPUS-MT model for session titles, workspace names, messages, search results, and opted-in plugin prose while keeping remote translation limited to allowlisted UI labels.", + "zh": "在 DeepSeek Harness Web 中翻译可见的中文界面与内容:会话标题、工作区名称、消息、搜索结果和插件主动标记的正文使用浏览器本地 OPUS-MT,远程翻译仅限白名单界面标签。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:RadicalGitter/dsh-ui-translate", + "added": "2026-08-23" + }, + { + "name": "dsh-desktop-windowos", + "owner": "RAFOLIE", + "url": "https://github.com/RAFOLIE/dsh-desktop-windowos", + "page": "https://awesome-dsh-plugin.com/p/RAFOLIE/dsh-desktop-windowos/", + "category": "ui", + "description": { + "en": "Windows tray desktop shell: auto-installs and auto-updates the exe from GitHub Releases, creates desktop shortcuts for both the app and the web UI, and exposes a desktop_launch tool to start it from the conversation.", + "zh": "Windows 托盘桌面壳:自动从 GitHub Releases 安装并升级 exe、创建应用与网页端两个桌面快捷方式,并提供 desktop_launch 工具在对话中一键启动。" + }, + "npm": "dsh-desktop-plugin", + "stars": 11, + "downloads": 3440, + "install": "dsh plugin --profile web add dsh-desktop-plugin", + "added": "2026-08-14" + }, + { + "name": "dsh-aseprite", + "owner": "Ratevoid", + "url": "https://github.com/Ratevoid/dsh-aseprite", + "page": "https://awesome-dsh-plugin.com/p/Ratevoid/dsh-aseprite/", + "category": "ui", + "description": { + "en": "A browser pixel-art and sprite animation editor with Aseprite-compatible files, local crop/outline blueprints, and optional LLM workflow nodes.", + "zh": "浏览器像素画与精灵动画编辑器,支持兼容 Aseprite 的文件、本地裁剪/描边蓝图和可选的 LLM 工作流节点。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:Ratevoid/dsh-aseprite", + "added": "2026-08-21" + }, + { + "name": "dsh-desktop#plugin", + "owner": "ReachGa0", + "url": "https://github.com/ReachGa0/dsh-desktop/tree/main/plugin", + "page": "https://awesome-dsh-plugin.com/p/ReachGa0/dsh-desktop--plugin/", + "category": "ui", + "description": { + "en": "DSH plugin (dsh-desktop-launcher) that launches the dsh-desktop Windows Electron shell from the conversation: double-click to run the DSH web in a standalone window, region screenshot with GPU-accelerated selection that auto-pastes into the chat (works with vision bridges), system tray, session manager, auto environment setup, configurable port, single-instance lock.", + "zh": "dsh 插件(dsh-desktop-launcher):在对话中一键启动 dsh-desktop Windows Electron 桌面壳——双击即用、无需终端;支持选区截图提问(GPU 实时框选、自动粘贴到聊天框、配合识图插件 AI 直接看图回答)、系统托盘、会话管理、环境自动引导、可换端口、单实例锁。" + }, + "npm": "dsh-desktop-windows-launcher", + "stars": 1, + "downloads": 179, + "install": "dsh plugin --profile web add dsh-desktop-windows-launcher", + "added": "2026-08-16" + }, + { + "name": "dsh-web-archive", + "owner": "renat3u", + "url": "https://github.com/renat3u/dsh-web-archive", + "page": "https://awesome-dsh-plugin.com/p/renat3u/dsh-web-archive/", + "category": "ui", + "description": { + "en": "Collapse noisy messages (Think, Bash, etc.) in conversations.", + "zh": "折叠对话中的 Think、Bash 等「无用消息」。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:renat3u/dsh-web-archive", + "added": "2026-08-13" + }, + { + "name": "dsh-workspace-scope", + "owner": "Ri0n72Y", + "url": "https://github.com/Ri0n72Y/dsh-workspace-scope", + "page": "https://awesome-dsh-plugin.com/p/Ri0n72Y/dsh-workspace-scope/", + "category": "ui", + "description": { + "en": "Per-workspace Skill and MCP enablement for DeepSeek Harness: control which skills and MCP servers new sessions load, per project.", + "zh": "按工作区(工程)启停 Skill 与 MCP:每个项目单独控制新会话加载哪些技能与 MCP 工具。" + }, + "npm": "dsh-workspace-scope", + "stars": 2, + "downloads": 595, + "install": "dsh plugin --profile web add dsh-workspace-scope", + "added": "2026-08-18" + }, + { + "name": "dsh-shortcuts", + "owner": "Ricketts-Guo", + "url": "https://github.com/Ricketts-Guo/dsh-shortcuts", + "page": "https://awesome-dsh-plugin.com/p/Ricketts-Guo/dsh-shortcuts/", + "category": "ui", + "description": { + "en": "Fully customizable keyboard shortcuts for the Web UI: 34 pre-registered features (sessions, views, clipboard, models, silent permission cycling, settings), one-click recording to bind your own, and a shortcut cheatsheet with built-in diagnostics.", + "zh": "Web UI 可自定义键盘快捷键:34 个预置功能(会话、视图、剪贴板、模型、静默权限切换、设置),一键录制自定义组合键,内置快捷键速查表与诊断面板。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:Ricketts-Guo/dsh-shortcuts", + "added": "2026-08-15" + }, + { + "name": "dshline#dshline", + "owner": "riesbri", + "url": "https://github.com/riesbri/dshline/tree/main/packages/dshline", + "page": "https://awesome-dsh-plugin.com/p/riesbri/dshline--packages-dshline/", + "category": "ui", + "description": { + "en": "Terminal-native TUI frontend for DeepSeek Harness that presents sessions, models, plugins, jobs, subagents, todos, and provider configuration through Harness capabilities while preserving real terminal scrollback.", + "zh": "DeepSeek Harness 的终端原生 TUI 前端,通过 Harness 能力提供会话、模型、插件、任务、子代理、待办事项和 Provider 配置,并保留真实的终端滚动历史。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:riesbri/dshline#path:/packages/dshline", + "added": "2026-08-27" + }, + { + "name": "dsh-annotation", + "owner": "ruisenbai", + "url": "https://github.com/ruisenbai/dsh-annotation", + "page": "https://awesome-dsh-plugin.com/p/ruisenbai/dsh-annotation/", + "category": "ui", + "description": { + "en": "Select text in a finished assistant reply to add numbered annotations beside the quote; drafts submit with composer text and images as one user message, and the model replies to each annotation in order.", + "zh": "在已完成的助手回复中选中原文,在引文旁添加编号注解;草稿与输入框文字、图片合并为一条用户消息发送,模型按注解逐条回复。" + }, + "npm": null, + "tarball": "https://github.com/ruisenbai/dsh-annotation/releases/download/v0.2.4/dsh-annotation-0.2.4.tgz", + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/ruisenbai/dsh-annotation/releases/download/v0.2.4/dsh-annotation-0.2.4.tgz\"", + "added": "2026-08-23" + }, + { + "name": "dsh-desktop#bundle", + "owner": "s3yf1337", + "url": "https://github.com/s3yf1337/dsh-desktop/tree/main/bundle", + "page": "https://awesome-dsh-plugin.com/p/s3yf1337/dsh-desktop--bundle/", + "category": "ui", + "description": { + "en": "Desktop profile for dsh: native Tauri window over the harness web surface with tray, native notifications, a file-manager panel, and one-click updates.", + "zh": "dsh 桌面配置:原生 Tauri 窗口承载 harness Web 界面,支持托盘、原生通知、文件管理器面板与一键更新。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:s3yf1337/dsh-desktop#path:/bundle", + "added": "2026-08-16" + }, + { + "name": "dsh-ctrl-enter-submit", + "owner": "SA1992X", + "url": "https://github.com/SA1992X/dsh-ctrl-enter-submit", + "page": "https://awesome-dsh-plugin.com/p/SA1992X/dsh-ctrl-enter-submit/", + "category": "ui", + "description": { + "en": "Makes plain Enter insert a newline in the composer and Ctrl/Cmd+Enter submit, toggleable in settings.", + "zh": "让对话框中普通 Enter 换行、Ctrl/Cmd+Enter 提交,可在设置中开关。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:SA1992X/dsh-ctrl-enter-submit", + "added": "2026-08-27" + }, + { + "name": "dsh-auto-paste", + "owner": "sakuraqqq", + "url": "https://github.com/sakuraqqq/dsh-auto-paste", + "page": "https://awesome-dsh-plugin.com/p/sakuraqqq/dsh-auto-paste/", + "category": "ui", + "description": { + "en": "Auto-saves large pasted text as an attachment file in the DeepSeek Harness web input box.", + "zh": "在输入框粘贴大段文本时自动保存为附件文件。" + }, + "npm": "dsh-auto-paste", + "stars": 2, + "downloads": 553, + "install": "dsh plugin --profile web add dsh-auto-paste", + "added": "2026-08-18" + }, + { + "name": "dsh-model-picker", + "owner": "Sanqi-normal", + "url": "https://github.com/Sanqi-normal/dsh-model-picker", + "page": "https://awesome-dsh-plugin.com/p/Sanqi-normal/dsh-model-picker/", + "category": "ui", + "description": { + "en": "Provider-tabbed model picker for the dsh web composer: left provider column and right model list, both independently scrollable, with cross-provider search.", + "zh": "dsh web 输入栏模型选择面板:左侧提供商栏 + 右侧模型列表,两侧独立滚动,支持跨提供商搜索。" + }, + "npm": "dsh-model-picker", + "stars": 3, + "downloads": 637, + "install": "dsh plugin --profile web add dsh-model-picker", + "added": "2026-08-15" + }, + { + "name": "dsh-sticker-board", + "owner": "sanqiPanax", + "url": "https://github.com/sanqiPanax/dsh-sticker-board", + "page": "https://awesome-dsh-plugin.com/p/sanqiPanax/dsh-sticker-board/", + "category": "ui", + "description": { + "en": "Fridge-magnet quick-task stickers docked above the composer: one-click sends a preloaded prompt into the current session, grouped long-term/short-term.", + "zh": "输入框上方的冰箱贴纸栏:把常用任务(科技新闻、GitHub 推荐等)做成贴纸,点击即向当前会话发送预置 prompt 并执行,按长期/短期分组。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:sanqiPanax/dsh-sticker-board", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/sanqiPanax/dsh-sticker-board/master/docs/screenshot-dock.png" + ] + }, + { + "name": "better-model-provider", + "owner": "sanshanya", + "url": "https://github.com/sanshanya/better-model-provider", + "page": "https://awesome-dsh-plugin.com/p/sanshanya/better-model-provider/", + "category": "ui", + "description": { + "en": "Settings page for per-model capabilities: reasoning-effort levels with their wire spellings, input modalities, and token capacities, written as declarations or sparse catalog overrides.", + "zh": "在设置页按模型编辑能力:推理档位及其发往端点的取值写法、输入模态与 token 容量,以声明或官方目录稀疏覆盖的形式写入。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:sanshanya/better-model-provider", + "added": "2026-08-24" + }, + { + "name": "dsh-sampling-sliders", + "owner": "Semidia", + "url": "https://github.com/Semidia/dsh-sampling-sliders", + "page": "https://awesome-dsh-plugin.com/p/Semidia/dsh-sampling-sliders/", + "category": "ui", + "description": { + "en": "Model sampling sliders (temperature / maxTokens) in the composer: override every model request for all providers via the agent/request hook, with hot/persist modes.", + "zh": "对话输入区的模型采样滑杆(temperature / maxTokens):通过 agent/request 钩子对所有供应商的每次请求生效,支持热调/持久化两种模式。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Semidia/dsh-sampling-sliders", + "added": "2026-08-15" + }, + { + "name": "dsh-desktop-pet", + "owner": "sereinmono", + "url": "https://github.com/sereinmono/dsh-desktop-pet", + "page": "https://awesome-dsh-plugin.com/p/sereinmono/dsh-desktop-pet/", + "category": "ui", + "description": { + "en": "A plugin that adds a desktop pet to your DeepSeek Harness, supporting the Codex pet format, you can use hatch-pet or Petdex to add your pets.", + "zh": "DeepSeek Harness 桌面宠物:完全支持 Codex 桌宠格式,可使用 hatch-pet Skill 创建宠物,或通过 Petdex 导入宠物。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:sereinmono/dsh-desktop-pet", + "added": "2026-08-14" + }, + { + "name": "dsh-plugin-msg-nav", + "owner": "SherUnlocked-4869", + "url": "https://github.com/SherUnlocked-4869/dsh-plugin-msg-nav", + "page": "https://awesome-dsh-plugin.com/p/SherUnlocked-4869/dsh-plugin-msg-nav/", + "category": "ui", + "description": { + "en": "Conversation node navigation rail for the DSH Web UI: one dash per user message on the right edge, reading-position tracking, hover preview card, click-to-jump with highlight line, sliding window, and auto-hide.", + "zh": "对话节点导航条:对话区右缘一列短横线节点对应每条用户消息,支持阅读位置跟踪、悬停预览卡片、点击跳转高亮、滑动窗口与自动隐藏。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:SherUnlocked-4869/dsh-plugin-msg-nav", + "added": "2026-08-15" + }, + { + "name": "dsh-compact-button", + "owner": "shyuan-hub", + "url": "https://github.com/shyuan-hub/dsh-compact-button", + "page": "https://awesome-dsh-plugin.com/p/shyuan-hub/dsh-compact-button/", + "category": "ui", + "description": { + "en": "Adds a Compact context button and a New session button to the context meter panel; compact submits /compact through the same command channel, and new session starts a fresh session in the same workspace.", + "zh": "在上下文计量面板中添加「压缩上下文」与「新建会话」两个按钮:压缩按钮通过与手敲 /compact 相同的通道提交命令,新建会话按钮在同一 workspace 中开启新会话。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:shyuan-hub/dsh-compact-button", + "added": "2026-08-25" + }, + { + "name": "dsh-plugin-terminal", + "owner": "siberiah2o", + "url": "https://github.com/siberiah2o/dsh-plugin-terminal", + "page": "https://awesome-dsh-plugin.com/p/siberiah2o/dsh-plugin-terminal/", + "category": "ui", + "description": { + "en": "Bottom multi-tab terminal panel (node-pty + xterm.js) pinned to the viewport bottom, always below the input box.", + "zh": "底部多标签终端面板(node-pty + xterm.js):贴底全宽,输入框始终在终端上方。" + }, + "npm": "dsh-plugin-terminal", + "stars": 6, + "downloads": 1498, + "install": "dsh plugin --profile web add dsh-plugin-terminal", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/siberiah2o/dsh-plugin-terminal/main/docs/screenshot-collapsed.png", + "https://raw.githubusercontent.com/siberiah2o/dsh-plugin-terminal/main/docs/screenshot-panel.png", + "https://raw.githubusercontent.com/siberiah2o/dsh-plugin-terminal/main/docs/screenshot-multitab.png" + ] + }, + { + "name": "dsh-approval-hotkeys", + "owner": "SiriLee", + "url": "https://github.com/SiriLee/dsh-approval-hotkeys", + "page": "https://awesome-dsh-plugin.com/p/SiriLee/dsh-approval-hotkeys/", + "category": "ui", + "description": { + "en": "Panel hotkeys for the web UI: Enter presses the confirm action and Esc presses the cancel action on every interaction panel (approval, question, plan review).", + "zh": "Web UI 面板快捷键:在审批、提问、计划审查等交互面板上,Enter 触发确认操作、Esc 触发取消操作。" + }, + "npm": "dsh-approval-hotkeys", + "stars": 2, + "downloads": 899, + "install": "dsh plugin --profile web add dsh-approval-hotkeys", + "added": "2026-08-20" + }, + { + "name": "dsh-client-ui-quote", + "owner": "skyhancloud", + "url": "https://github.com/skyhancloud/dsh-client-ui-quote", + "page": "https://awesome-dsh-plugin.com/p/skyhancloud/dsh-client-ui-quote/", + "category": "ui", + "description": { + "en": "Adds a removable quote banner above the web chat composer for text selected from an AI reply, included in the next message.", + "zh": "在网页聊天输入框上方为选中的 AI 回复内容添加可移除的引用横幅,随下一条消息一并发送。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:skyhancloud/dsh-client-ui-quote", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/skyhancloud/dsh-client-ui-quote/main/assets/preview-1.png", + "https://raw.githubusercontent.com/skyhancloud/dsh-client-ui-quote/main/assets/preview-2.png" + ] + }, + { + "name": "dsh-planchart", + "owner": "SmileBuild", + "url": "https://github.com/SmileBuild/dsh-planchart", + "page": "https://awesome-dsh-plugin.com/p/SmileBuild/dsh-planchart/", + "category": "ui", + "description": { + "en": "Publishes a project plan the agent writes: ordered steps in a panel docked to the right of the frame, and the framework graph as an auto-laid-out diagram in its own centre tab, downloadable as SVG or PNG. Charts are rebuilt from the session log, so they survive restart and resume.", + "zh": "把 agent 写出的项目规划显示出来:有序步骤在窗口右侧停靠的面板里,框架图在单独的中间标签页中自动排版,可下载为 SVG 或 PNG。图表由会话日志重建,重启与恢复后依然保留。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:SmileBuild/dsh-planchart", + "added": "2026-08-21", + "screenshots": [ + "https://raw.githubusercontent.com/SmileBuild/dsh-planchart/main/assets/screenshot-1.png", + "https://raw.githubusercontent.com/SmileBuild/dsh-planchart/main/assets/screenshot-2.png" + ] + }, + { + "name": "dsh-milestone", + "owner": "SnowCrescenter-tech", + "url": "https://github.com/SnowCrescenter-tech/dsh-milestone", + "page": "https://awesome-dsh-plugin.com/p/SnowCrescenter-tech/dsh-milestone/", + "category": "ui", + "description": { + "en": "Git-style dot timeline on the right edge of the DSH web UI: one dot per user message, hover for content and metadata (time, turn, duration, TTFT, tokens), click to jump. Also adds a full-session message list, in-session and cross-session search, bookmarks with #msg= deep links, and keyboard navigation.", + "zh": "DSH Web UI 右侧的圆点时间轴:每条提问一个圆点,悬停查看内容与元信息(时间、轮次、耗时、TTFT、token),点击即跳转。附带全会话消息列表、站内与跨会话搜索、书签深链接(#msg=)和键盘导航。" + }, + "npm": null, + "stars": 20, + "downloads": null, + "install": "dsh plugin --profile web add github:SnowCrescenter-tech/dsh-milestone", + "added": "2026-08-13" + }, + { + "name": "dsh-prompt-optimizer", + "owner": "SongMiao-tech", + "url": "https://github.com/SongMiao-tech/dsh-prompt-optimizer", + "page": "https://awesome-dsh-plugin.com/p/SongMiao-tech/dsh-prompt-optimizer/", + "category": "ui", + "description": { + "en": "Optimize Prompt button under the composer: one click rewrites your draft into a clearer, more executable prompt, with a before-and-after dialog and one-click replacement.", + "zh": "输入框下方「优化提示词」按钮:一键把草稿重写为更清晰、更可执行的提示词,弹出前后对比,支持一键替换回输入框。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:SongMiao-tech/dsh-prompt-optimizer", + "added": "2026-08-15" + }, + { + "name": "dsh-bottom-info-bar#plugin", + "owner": "songoao25", + "url": "https://github.com/songoao25/dsh-bottom-info-bar/tree/main/plugin", + "page": "https://awesome-dsh-plugin.com/p/songoao25/dsh-bottom-info-bar--plugin/", + "category": "ui", + "description": { + "en": "Bottom info bar for the DSH Web UI: provider/model, live balance, peak/off-peak pricing with countdown, and real persisted per-session spend in a single line.", + "zh": "DSH 底部信息栏:一行显示服务商/模型、真实余额、高峰(琥珀)/空闲(绿)价与倒计时,以及本对话·今日·近一月·全部花费(真实落盘统计)。" + }, + "npm": "dsh-bottom-info-bar", + "stars": 16, + "downloads": 922, + "install": "dsh plugin --profile web add dsh-bottom-info-bar", + "added": "2026-08-16" + }, + { + "name": "dsh-plugin-smooth-stream", + "owner": "SpookySandwich", + "url": "https://github.com/SpookySandwich/dsh-plugin-smooth-stream", + "page": "https://awesome-dsh-plugin.com/p/SpookySandwich/dsh-plugin-smooth-stream/", + "category": "ui", + "description": { + "en": "Better streaming text animation for DeepSeek Harness.", + "zh": "给 DeepSeek Harness 加入更好的流式文字动画。" + }, + "npm": "dsh-plugin-smooth-stream", + "stars": 9, + "downloads": 777, + "install": "dsh plugin --profile web add dsh-plugin-smooth-stream", + "added": "2026-08-16" + }, + { + "name": "dsh-skin-universe#dsh-web-ui-all", + "owner": "springbrand-lab", + "url": "https://github.com/springbrand-lab/dsh-skin-universe/tree/main/packages/dsh-web-ui-all", + "page": "https://awesome-dsh-plugin.com/p/springbrand-lab/dsh-skin-universe--packages-dsh-web-ui-all/", + "category": "ui", + "description": { + "en": "Web GUI bundle for DSH: a task board with cron scheduling, a Git branch graph, a file and change side panel, an SSH terminal with SFTP and port forwarding, QR-paired mobile remote control, a desktop companion, live token statistics, and five installable themes.", + "zh": "DSH Web GUI 全家桶:带定时调度的任务看板、Git 分支图、文件与变更侧栏、支持 SFTP 与端口转发的 SSH 终端、扫码配对的手机远程控制、桌面宠物、实时 token 统计,以及五套可安装主题。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:springbrand-lab/dsh-skin-universe#path:/packages/dsh-web-ui-all", + "added": "2026-08-17" + }, + { + "name": "dsh-workspace-files", + "owner": "sqfcyily", + "url": "https://github.com/sqfcyily/dsh-workspace-files", + "page": "https://awesome-dsh-plugin.com/p/sqfcyily/dsh-workspace-files/", + "category": "ui", + "description": { + "en": "Browse the directories/files of the workspace in DeepSeek Harness Web GUI and display file changes in conjunction with Git.", + "zh": "在DeepSeek Harness Web GUI 中浏览工作区的目录/文件,并结合 Git 显示文件改动。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:sqfcyily/dsh-workspace-files", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/sqfcyily/dsh-workspace-files/HEAD/docs/155158.png" + ] + }, + { + "name": "dsh-suggest-prompt", + "owner": "studyzy", + "url": "https://github.com/studyzy/dsh-suggest-prompt", + "page": "https://awesome-dsh-plugin.com/p/studyzy/dsh-suggest-prompt/", + "category": "ui", + "description": { + "en": "After each completed agent turn, a bounded auxiliary LLM call writes one suggested next prompt into the session log; the web composer renders it as ghost placeholder text, adopted via Tab.", + "zh": "每次完成的 agent 回合后,一次有界的辅助 LLM 调用把一条建议的下一句 prompt 写入会话日志;Web 输入框将其渲染为幽灵占位文字,按 Tab 采纳。" + }, + "npm": "@studyzy/dsh-suggest-prompt", + "stars": 3, + "downloads": 358, + "install": "dsh plugin --profile web add @studyzy/dsh-suggest-prompt", + "added": "2026-08-24" + }, + { + "name": "dsh-chat-history", + "owner": "Stylelinzzz", + "url": "https://github.com/Stylelinzzz/dsh-chat-history", + "page": "https://awesome-dsh-plugin.com/p/Stylelinzzz/dsh-chat-history/", + "category": "ui", + "description": { + "en": "Adds a \"History\" conversation view tab listing the session's user messages as a clickable directory, with auto-paging and click-to-jump back into the chat.", + "zh": "在会话头部新增「目录」tab:把当前会话的用户消息整理成可点击的目录,自动翻页加载全部历史,点击即跳回对话并定位。" + }, + "npm": "dsh-chat-history", + "stars": 1, + "downloads": 361, + "install": "dsh plugin --profile web add dsh-chat-history", + "added": "2026-08-19" + }, + { + "name": "dsh-selection-toolbar", + "owner": "suiyideali", + "url": "https://github.com/suiyideali/dsh-selection-toolbar", + "page": "https://awesome-dsh-plugin.com/p/suiyideali/dsh-selection-toolbar/", + "category": "ui", + "description": { + "en": "Shows a floating toolbar above text selected in a conversation, with copy, quote, ask, explain, translate, summarize, and custom prompt actions.", + "zh": "在对话中选中文本时显示浮动工具栏,提供复制、引用、询问、解释、翻译、总结与自定义提问操作。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:suiyideali/dsh-selection-toolbar", + "added": "2026-08-19" + }, + { + "name": "dsh-file-mention", + "owner": "sunshaobei", + "url": "https://github.com/sunshaobei/dsh-file-mention", + "page": "https://awesome-dsh-plugin.com/p/sunshaobei/dsh-file-mention/", + "category": "ui", + "description": { + "en": "Codex-style @file/@folder mentions for the DSH web composer: workspace file/folder picker menu, plain-text colored mention tokens via the platform lexicon scan, and a nested-git-aware file index.", + "zh": "Codex 式 @文件/@文件夹提及插件:输入框弹出工作区文件与文件夹选择菜单,通过平台词法扫描插入纯文本着色提及 token,内置嵌套 git 感知的文件索引。" + }, + "npm": "dsh-file-mention", + "stars": 1, + "downloads": 378, + "install": "dsh plugin --profile web add dsh-file-mention", + "added": "2026-08-18" + }, + { + "name": "dsh-input-history", + "owner": "sunshaobei", + "url": "https://github.com/sunshaobei/dsh-input-history", + "page": "https://awesome-dsh-plugin.com/p/sunshaobei/dsh-input-history/", + "category": "ui", + "description": { + "en": "Standalone arrow-key input history for the DSH web composer: ↑/↓ recalls sent prompts from one global ring shared across all sessions and workspaces, persisted to localStorage with an overridable cap.", + "zh": "DSH 网页版输入框独立输入历史插件:↑/↓ 上下键即时召回已发送的提示词,所有会话与工作区共享一个全局历史环,localStorage 持久化、容量可配。" + }, + "npm": "dsh-input-history", + "stars": 1, + "downloads": 337, + "install": "dsh plugin --profile web add dsh-input-history", + "added": "2026-08-18" + }, + { + "name": "dsh-files", + "owner": "taxueseek", + "url": "https://github.com/taxueseek/dsh-files", + "page": "https://awesome-dsh-plugin.com/p/taxueseek/dsh-files/", + "category": "ui", + "description": { + "en": "File upload with color-coded attachment cards (session-isolated storage, sha256 dedup, TTL sweep) plus a content-sniffing read_document tool for PDF/DOCX/XLSX/TXT.", + "zh": "文件上传(彩色附件卡片、会话隔离存储、sha256 去重、TTL 清扫)+ 内容嗅探的 read_document 文档读取(PDF/DOCX/XLSX/TXT)。" + }, + "npm": null, + "stars": 26, + "downloads": null, + "install": "dsh plugin --profile web add github:taxueseek/dsh-files", + "added": "2026-08-14" + }, + { + "name": "dsh-mobile", + "owner": "TecFancy", + "url": "https://github.com/TecFancy/dsh-mobile", + "page": "https://awesome-dsh-plugin.com/p/TecFancy/dsh-mobile/", + "category": "ui", + "description": { + "en": "Mobile adapter for the DSH web shell: overlay sidebar/details drawers, responsive composer and settings below 768px, zero desktop regression.", + "zh": "DSH Web 移动端适配插件:侧边栏/详情抽屉浮层化、输入栏与设置页响应式适配,桌面零回归。" + }, + "npm": "@tecfancy/dsh-mobile", + "stars": 1, + "downloads": 1270, + "install": "dsh plugin --profile web add @tecfancy/dsh-mobile", + "added": "2026-08-16" + }, + { + "name": "dsh-wide-stats-footer", + "owner": "thomasvvugt", + "url": "https://github.com/thomasvvugt/dsh-wide-stats-footer", + "page": "https://awesome-dsh-plugin.com/p/thomasvvugt/dsh-wide-stats-footer/", + "category": "ui", + "description": { + "en": "Un-clamps the composer stats footer: long turn and token stats lines span the full composer width instead of truncating with an ellipsis.", + "zh": "解除输入框统计行的宽度限制:较长的轮次与 token 统计行横跨输入框全宽居中显示,不再以省略号截断。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:thomasvvugt/dsh-wide-stats-footer", + "added": "2026-08-26" + }, + { + "name": "dsh-wallpaper-engine#plugin", + "owner": "TianYa-DAO", + "url": "https://github.com/TianYa-DAO/dsh-wallpaper-engine/tree/main/packages/dsh-wallpaper-engine", + "page": "https://awesome-dsh-plugin.com/p/TianYa-DAO/dsh-wallpaper-engine--packages-dsh-wallpaper-engine/", + "category": "ui", + "description": { + "en": "Wallpaper Engine local library, background layer, native Scene panel, and desktop-mode controls for the DSH desktop shell.", + "zh": "面向 DSH 桌面壳的 Wallpaper Engine 本地库、背景层、原生 Scene 面板与桌面模式控件。" + }, + "npm": null, + "tarball": "https://github.com/TianYa-DAO/dsh-wallpaper-engine/releases/download/dsh-0.1.2/dsh-wallpaper-engine-0.1.2.tgz", + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/TianYa-DAO/dsh-wallpaper-engine/releases/download/dsh-0.1.2/dsh-wallpaper-engine-0.1.2.tgz\"", + "added": "2026-08-18" + }, + { + "name": "dsh-vscode-workbench", + "owner": "tingfeng347", + "url": "https://github.com/tingfeng347/dsh-vscode-workbench", + "page": "https://awesome-dsh-plugin.com/p/tingfeng347/dsh-vscode-workbench/", + "category": "ui", + "description": { + "en": "VS Code-style local workbench for DSH with a file explorer, Monaco editor, full-text search and a Git panel.", + "zh": "为 DSH 提供 VS Code 风格的本地工作台,含文件资源管理器、Monaco 编辑器、全文搜索与 Git 面板。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:tingfeng347/dsh-vscode-workbench", + "added": "2026-08-26" + }, + { + "name": "dsh-file-mention", + "owner": "tipoLi5890", + "url": "https://github.com/tipoLi5890/dsh-file-mention", + "page": "https://awesome-dsh-plugin.com/p/tipoLi5890/dsh-file-mention/", + "category": "ui", + "description": { + "en": "Session-scoped @file/@folder mentions in the composer with Git-aware indexing, drag-drop and paste managed uploads, and validated path markers.", + "zh": "输入框内 session 范围的 @文件/@文件夹提及:Git 感知索引、拖放与粘贴受管上传、经校验的路径标记。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:tipoLi5890/dsh-file-mention", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/tipoLi5890/dsh-file-mention/main/assets/screenshots/file-mention-browse.png", + "https://raw.githubusercontent.com/tipoLi5890/dsh-file-mention/main/assets/screenshots/file-mention-drag-drop.png", + "https://raw.githubusercontent.com/tipoLi5890/dsh-file-mention/main/assets/screenshots/file-mention-upload-reference.png" + ] + }, + { + "name": "dsh-media-preview", + "owner": "tsonglew", + "url": "https://github.com/tsonglew/dsh-media-preview", + "page": "https://awesome-dsh-plugin.com/p/tsonglew/dsh-media-preview/", + "category": "ui", + "description": { + "en": "Audio/video FileViewer for dsh-better-sidebar: native inline playback for mp4/webm/mkv/mov and mp3/flac/wav, served by a Range-capable streaming media route.", + "zh": "better-sidebar 音视频预览器:原生播放器内联播放 mp4/webm/mkv/mov 等视频与 mp3/flac/wav 等音频,自带支持 HTTP Range 拖动的流式媒体路由。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:tsonglew/dsh-media-preview", + "added": "2026-08-14" + }, + { + "name": "dsh-workspace-search", + "owner": "tsonglew", + "url": "https://github.com/tsonglew/dsh-workspace-search", + "page": "https://awesome-dsh-plugin.com/p/tsonglew/dsh-workspace-search/", + "category": "ui", + "description": { + "en": "VS Code-style workspace keyword search tab for dsh-better-sidebar: matches file names and content, grouped by file with line numbers, opens in the sidebar editor.", + "zh": "VS Code 式工作区关键词搜索 Tab(better-sidebar 扩展):同时匹配文件名与文件内容,结果按文件分组带行号,点击在侧栏编辑器打开。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:tsonglew/dsh-workspace-search", + "added": "2026-08-14" + }, + { + "name": "dsh-word-complete", + "owner": "tuogusa", + "url": "https://github.com/tuogusa/dsh-word-complete", + "page": "https://awesome-dsh-plugin.com/p/tuogusa/dsh-word-complete/", + "category": "ui", + "description": { + "en": "Word auto-completion in the Web composer: prefix-matched suggestions sorted by closeness, arrow-key selection and Enter to confirm.", + "zh": "输入框词自动补全:按前缀匹配与接近程度排序弹出当前会话词建议,↑/↓ 选择、回车确认。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:tuogusa/dsh-word-complete", + "added": "2026-08-20" + }, + { + "name": "dsh-plugins#dsh-mobile-ui", + "owner": "TZHR-invest", + "url": "https://github.com/TZHR-invest/dsh-plugins/tree/main/packages/dsh-mobile-ui", + "page": "https://awesome-dsh-plugin.com/p/TZHR-invest/dsh-plugins--packages-dsh-mobile-ui/", + "category": "ui", + "description": { + "en": "Mobile UI for the Web GUI on narrow screens: full-width responsive layout, overlay session drawer, 44px touch targets, safe-area support, and reading enhancements with zero desktop impact.", + "zh": "Web GUI 移动端适配:窄屏全宽响应式布局、会话抽屉、44px 触摸目标、安全区适配与阅读增强,桌面端零影响。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:TZHR-invest/dsh-plugins#path:/packages/dsh-mobile-ui", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/TZHR-invest/dsh-plugins/main/packages/dsh-mobile-ui/assets/screenshot-1-home.png", + "https://raw.githubusercontent.com/TZHR-invest/dsh-plugins/main/packages/dsh-mobile-ui/assets/screenshot-2-composer.png", + "https://raw.githubusercontent.com/TZHR-invest/dsh-plugins/main/packages/dsh-mobile-ui/assets/screenshot-3-chat.png", + "https://raw.githubusercontent.com/TZHR-invest/dsh-plugins/main/packages/dsh-mobile-ui/assets/screenshot-4-sessions.png" + ] + }, + { + "name": "dsh-valley-meter", + "owner": "uckkk", + "url": "https://github.com/uckkk/dsh-valley-meter", + "page": "https://awesome-dsh-plugin.com/p/uckkk/dsh-valley-meter/", + "category": "ui", + "description": { + "en": "Peak-Valley Meter (峰谷电表): live off-peak countdown on a 24h timeline with hourly spend blocks, official DeepSeek account balance, today's spend, 10 curated muted color presets (One Dark/Dracula/Nord/Tokyo Night/Gruvbox/Solarized, traditional Chinese & Pantone), minimal-by-default card, all-Chinese UI.", + "zh": "峰谷电表——波谷倒计时小组件:24 小时时间轴上的峰谷时段与按小时消耗方块阵列、官方账户余额、今日花费;内置 10 套沉稳配色预置(One Dark/Dracula/Nord/Tokyo Night/Gruvbox/Solarized + 中国传统色/潘通),默认极简卡片,全中文界面。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:uckkk/dsh-valley-meter", + "added": "2026-08-26" + }, + { + "name": "dsh-process-fold", + "owner": "uigdwunm", + "url": "https://github.com/uigdwunm/dsh-process-fold", + "page": "https://awesome-dsh-plugin.com/p/uigdwunm/dsh-process-fold/", + "category": "ui", + "description": { + "en": "Groups each DSH Web turn's thinking, tool calls and context into an expandable process box that shows the latest two items by default, while keeping user dialogue, interactive questions and final answers outside.", + "zh": "把每轮 DSH Web 会话中的思考、工具调用和上下文合并为可展开的过程框,默认显示最新两项,同时把用户对话、交互式提问和最终回答留在框外。" + }, + "npm": null, + "tarball": "https://github.com/uigdwunm/dsh-process-fold/releases/latest/download/dsh-process-fold-0.1.1.tgz", + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/uigdwunm/dsh-process-fold/releases/latest/download/dsh-process-fold-0.1.1.tgz\"", + "added": "2026-08-18" + }, + { + "name": "dsh-think-translate", + "owner": "UncleK", + "url": "https://github.com/UncleK/dsh-think-translate", + "page": "https://awesome-dsh-plugin.com/p/UncleK/dsh-think-translate/", + "category": "ui", + "description": { + "en": "Display-layer translation for the DSH Web UI — thinking chains, task cards and answers in 8 target languages via local Ollama or Google/Bing, originals preserved.", + "zh": "DSH Web 界面显示层翻译:将思考链、任务卡片与回答正文翻译为 8 种目标语言之一(本地 Ollama 或 Google/Bing,自动选择),原文完整保留。" + }, + "npm": null, + "tarball": "https://github.com/UncleK/dsh-think-translate/releases/download/v1.0.10/dsh-think-translate-1.0.10.tgz", + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/UncleK/dsh-think-translate/releases/download/v1.0.10/dsh-think-translate-1.0.10.tgz\"", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/UncleK/dsh-think-translate/HEAD/demo/demo.gif", + "https://raw.githubusercontent.com/UncleK/dsh-think-translate/HEAD/demo/demo2.gif" + ] + }, + { + "name": "dsh-file-preview#dsh-file-preview", + "owner": "UndeadSheep", + "url": "https://github.com/UndeadSheep/dsh-file-preview/tree/main/packages/dsh-file-preview", + "page": "https://awesome-dsh-plugin.com/p/UndeadSheep/dsh-file-preview--packages-dsh-file-preview/", + "category": "ui", + "description": { + "en": "Floating workspace file-preview window for the Web client: lazy file tree, CodeMirror preview and in-window edit with save, Markdown and image preview, Quick Open search, and opening paths clicked in the conversation.", + "zh": "Web 客户端悬浮文件预览窗:懒加载文件树、CodeMirror 预览与窗内编辑保存、Markdown 与图片预览、Quick Open 搜索,以及点击会话中的路径直接打开。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:UndeadSheep/dsh-file-preview#path:/packages/dsh-file-preview", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/UndeadSheep/dsh-file-preview/main/assets/github-social-preview.png", + "https://raw.githubusercontent.com/UndeadSheep/dsh-file-preview/main/assets/btnPos.gif", + "https://raw.githubusercontent.com/UndeadSheep/dsh-file-preview/main/assets/write.gif", + "https://raw.githubusercontent.com/UndeadSheep/dsh-file-preview/main/assets/clickOpen.gif", + "https://raw.githubusercontent.com/UndeadSheep/dsh-file-preview/main/assets/SearchBar.gif" + ] + }, + { + "name": "dsh-outline", + "owner": "urzeye", + "url": "https://github.com/urzeye/dsh-outline", + "page": "https://awesome-dsh-plugin.com/p/urzeye/dsh-outline/", + "category": "ui", + "description": { + "en": "Real-time outline panel for the DSH Web session page: user questions plus a Markdown heading tree (H1–H6) that updates live during streaming, with click-to-locate highlighting, expand-depth control, search, and per-session favorites.", + "zh": "DSH Web 会话页实时大纲面板:「用户问题 + Markdown 标题(1~6 级)」大纲树,流式生成时实时更新,点击节点滚动定位并高亮,支持展开深度调节、搜索与会话级收藏。" + }, + "npm": "dsh-outline", + "stars": 14, + "downloads": 1275, + "install": "dsh plugin --profile web add dsh-outline", + "added": "2026-08-14" + }, + { + "name": "dsh-turn-navigator", + "owner": "vibeinging", + "url": "https://github.com/vibeinging/dsh-turn-navigator", + "page": "https://awesome-dsh-plugin.com/p/vibeinging/dsh-turn-navigator/", + "category": "ui", + "description": { + "en": "Turn navigation for the DSH Web UI.", + "zh": "对话轮次导航。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:vibeinging/dsh-turn-navigator", + "added": "2026-08-13" + }, + { + "name": "dsh-navbar", + "owner": "vlln", + "url": "https://github.com/vlln/dsh-navbar", + "page": "https://awesome-dsh-plugin.com/p/vlln/dsh-navbar/", + "category": "ui", + "description": { + "en": "Conversation node navigation bar for quick jumps between user messages.", + "zh": "对话节点导航条,右缘节点串快速跳转 user 消息。" + }, + "npm": null, + "stars": 59, + "downloads": null, + "install": "dsh plugin --profile web add github:vlln/dsh-navbar", + "added": "2026-08-13" + }, + { + "name": "dsh-task-status", + "owner": "vlln", + "url": "https://github.com/vlln/dsh-task-status", + "page": "https://awesome-dsh-plugin.com/p/vlln/dsh-task-status/", + "category": "ui", + "description": { + "en": "Background task status bar: progress plus live output tail on the chat page.", + "zh": "后台任务状态条:对话页任务进度 + 实时输出 tail。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:vlln/dsh-task-status", + "added": "2026-08-13" + }, + { + "name": "better-reasoning-slider", + "owner": "vvvspec", + "url": "https://github.com/vvvspec/better-reasoning-slider", + "page": "https://awesome-dsh-plugin.com/p/vvvspec/better-reasoning-slider/", + "category": "ui", + "description": { + "en": "Official-style composer model trigger with a floating reasoning-effort slider popup.", + "zh": "官方风格输入框 + 浮动推理能力滑块弹窗,节点与滑块对齐,弹窗跟随 Harness 主题自适应。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:vvvspec/better-reasoning-slider", + "added": "2026-08-16" + }, + { + "name": "dsh-web-icon-indicator", + "owner": "waknow", + "url": "https://github.com/waknow/dsh-web-icon-indicator", + "page": "https://awesome-dsh-plugin.com/p/waknow/dsh-web-icon-indicator/", + "category": "ui", + "description": { + "en": "Browser tab favicon mirrors the DSH session state (idle / running / asking / done), recolored and animated from one base SVG entirely in the browser, with colors and effects configurable from the Settings page.", + "zh": "浏览器标签页 favicon 实时反映 DSH 会话状态(待机 / 运行中 / 提问 / 完成):单个 base.svg 在浏览器端上色与动画,颜色与特效均可在设置页配置。" + }, + "npm": "dsh-web-icon-indicator", + "stars": 1, + "downloads": 693, + "install": "dsh plugin --profile web add dsh-web-icon-indicator", + "added": "2026-08-22" + }, + { + "name": "dsh-ui-font", + "owner": "warmwine", + "url": "https://github.com/warmwine/dsh-ui-font", + "page": "https://awesome-dsh-plugin.com/p/warmwine/dsh-ui-font/", + "category": "ui", + "description": { + "en": "Font engine for the Web GUI: system font enumeration, global and per-component font-size tuning with a Spy++-style picker, settings page.", + "zh": "网页界面字体引擎:系统字体枚举、准星点选的全局与逐组件字号微调、设置页。老花眼友好,自定义字体和页面部件字体和字体大小,支持SPY模式扫UI和其他插件。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:warmwine/dsh-ui-font", + "added": "2026-08-16" + }, + { + "name": "dsh-scratchpad", + "owner": "Waverly-W", + "url": "https://github.com/Waverly-W/dsh-scratchpad", + "page": "https://awesome-dsh-plugin.com/p/Waverly-W/dsh-scratchpad/", + "category": "ui", + "description": { + "en": "Quick temporary conversation and workspace manager for DeepSeek Harness Web GUI.", + "zh": "DeepSeek Harness Web GUI 临时对话工作区自动化管理插件。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Waverly-W/dsh-scratchpad", + "added": "2026-08-21" + }, + { + "name": "dsh-cache-miss", + "owner": "wefio", + "url": "https://github.com/wefio/dsh-cache-miss", + "page": "https://awesome-dsh-plugin.com/p/wefio/dsh-cache-miss/", + "category": "ui", + "description": { + "en": "Yellow one-line notice for a prompt cache miss after the cache was rebuilt; shows re-billed uncached input tokens, cached hit tokens, and TTFT.", + "zh": "提示缓存未命中(cache miss)时,在重建缓存的 assistant 回复下方显示一条黄色缓存提示,展示未缓存(uncached)重新计费(re-billed)的输入 token、缓存命中 token 与首字延迟(TTFT)。" + }, + "npm": "dsh-cache-miss", + "stars": 2, + "downloads": 715, + "install": "dsh plugin --profile web add dsh-cache-miss", + "added": "2026-08-16" + }, + { + "name": "dsh-conversation-density-map", + "owner": "weien666", + "url": "https://github.com/weien666/dsh-conversation-density-map", + "page": "https://awesome-dsh-plugin.com/p/weien666/dsh-conversation-density-map/", + "category": "ui", + "description": { + "en": "Adds a non-uniform conversation density map(chat history markers) to the right of the DSH chat area: one tick per turn, with tick length and spacing reflecting message size, clickable to jump to a turn and expanding on hover when crowded.", + "zh": "在 DSH 聊天区右侧添加非等长非等距对话密度地图(历史聊天标签),一轮对话一个刻度,刻度长度与纵向距离反映消息大小,点击刻度可跳转至对应对话,标签繁杂时鼠标悬停会自动拉伸便于点击。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:weien666/dsh-conversation-density-map", + "added": "2026-08-20" + }, + { + "name": "weshop-dsh-plugin", + "owner": "weshopai", + "url": "https://github.com/weshopai/weshop-dsh-plugin", + "page": "https://awesome-dsh-plugin.com/p/weshopai/weshop-dsh-plugin/", + "category": "ui", + "description": { + "en": "Infinite-canvas e-commerce visual workspace for product photography, virtual try-on, background edits, and video, synced live with the Harness conversation.", + "zh": "电商可视化工作区:在无限画布上完成产品图、虚拟试穿、背景处理与视频生成,并与 Harness 对话实时同步。" + }, + "npm": "weshop-dsh-plugin", + "stars": 13, + "downloads": 433, + "install": "dsh plugin --profile web add weshop-dsh-plugin", + "added": "2026-08-17" + }, + { + "name": "dsh-openbiliclaw", + "owner": "whiteguo233", + "url": "https://github.com/whiteguo233/dsh-openbiliclaw", + "page": "https://awesome-dsh-plugin.com/p/whiteguo233/dsh-openbiliclaw/", + "category": "ui", + "description": { + "en": "OpenBiliClaw consumer panel for DSH: recommendations, saved lists, Socratic dialogue, profile views, and Agent Bridge tools.", + "zh": "OpenBiliClaw 的 DSH 消费侧插件:提供推荐、收藏列表、苏格拉底式对话、画像面板和 Agent Bridge 工具。" + }, + "npm": null, + "stars": 53, + "downloads": null, + "install": "dsh plugin --profile web add github:whiteguo233/dsh-openbiliclaw", + "added": "2026-08-16" + }, + { + "name": "dsh-input-plus", + "owner": "WhitePlusMS", + "url": "https://github.com/WhitePlusMS/dsh-input-plus", + "page": "https://awesome-dsh-plugin.com/p/WhitePlusMS/dsh-input-plus/", + "category": "ui", + "description": { + "en": "Search and insert workspace file and directory paths with `@`, plus a `/h` menu for reusing prompts from the current session.", + "zh": "在组合框中使用 `@` 搜索并插入工作区文件和目录路径,并通过 `/h` 菜单复用当前会话的问题。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:WhitePlusMS/dsh-input-plus", + "added": "2026-08-14" + }, + { + "name": "dsh-wsl-workspace-picker", + "owner": "WilliamShi666", + "url": "https://github.com/WilliamShi666/dsh-wsl-workspace-picker", + "page": "https://awesome-dsh-plugin.com/p/WilliamShi666/dsh-wsl-workspace-picker/", + "category": "ui", + "description": { + "en": "Enhanced workspace directory browser for the web UI: one-click access to /mnt Windows drives under WSL, full breadcrumb ancestry and an always-visible path input.", + "zh": "Web UI 增强版工作区目录选择器:WSL 下 /mnt Windows 盘符一键直达、完整面包屑层级与常显路径输入框。" + }, + "npm": "dsh-wsl-workspace-picker", + "stars": 1, + "downloads": 320, + "install": "dsh plugin --profile web add dsh-wsl-workspace-picker", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/WilliamShi666/dsh-wsl-workspace-picker/main/assets/workspace-picker-demo.png" + ] + }, + { + "name": "dsh-elf", + "owner": "winditer", + "url": "https://github.com/winditer/dsh-elf", + "page": "https://awesome-dsh-plugin.com/p/winditer/dsh-elf/", + "category": "ui", + "description": { + "en": "Floating translucent DeepSeek whale elf on the DSH page that opens a draggable, minimizable temporary chat window using the session model or a custom OpenAI-compatible endpoint, with real-time streaming.", + "zh": "DSH 页面悬浮的半透明 DeepSeek 鲸鱼小精灵:点击打开可拖动、可最小化的临时聊天浮窗,支持跟随会话模型或自定义 OpenAI 兼容端点实时流式输出。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:winditer/dsh-elf", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/winditer/dsh-elf/main/assets/screenshot-elf.png", + "https://raw.githubusercontent.com/winditer/dsh-elf/main/assets/screenshot-chat.png" + ] + }, + { + "name": "dsh-prompt-optimizer", + "owner": "winditer", + "url": "https://github.com/winditer/dsh-prompt-optimizer", + "page": "https://awesome-dsh-plugin.com/p/winditer/dsh-prompt-optimizer/", + "category": "ui", + "description": { + "en": "Polishes the draft in the composer into a clearer, better-structured prompt from the ✨ button or Alt+O; streams the result live over SSE with reasoning shown first, and follows the session's default model with zero configuration or any OpenAI-compatible endpoint.", + "zh": "在输入框按 ✨ 或 Alt+O 一键把草稿润色成更清晰、更结构化的 prompt;默认零配置复用当前会话模型(SSE 真流式、推理过程先出),也可自配任意 OpenAI 兼容端点。" + }, + "npm": "dsh-prompt-optimizer", + "stars": 1, + "downloads": 519, + "install": "dsh plugin --profile web add dsh-prompt-optimizer", + "added": "2026-08-19" + }, + { + "name": "dsh-code-studio", + "owner": "WindyPro-rourou", + "url": "https://github.com/WindyPro-rourou/dsh-code-studio", + "page": "https://awesome-dsh-plugin.com/p/WindyPro-rourou/dsh-code-studio/", + "category": "ui", + "description": { + "en": "File-change monitor and code editor for the DSH Web UI. Agent edits appear beside your conversation as real-time line-by-line diffs, with a syntax-highlighted editor, one-click revert, change history, workspace switcher, and per-session isolation.", + "zh": "DSH Web UI 的文件修改监视器与代码工作台。Agent 修改代码时实时浮现逐行 Diff,内置语法高亮编辑器,支持一键还原、修改历史、工作区切换与按会话隔离。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:WindyPro-rourou/dsh-code-studio", + "added": "2026-08-26" + }, + { + "name": "dsh-codex-timeline", + "owner": "Wine-Red", + "url": "https://github.com/Wine-Red/dsh-codex-timeline", + "page": "https://awesome-dsh-plugin.com/p/Wine-Red/dsh-codex-timeline/", + "category": "ui", + "description": { + "en": "Left-edge user-Turn navigation rail with reading-position highlighting, hover previews of per-turn metrics and model-answer excerpts, keyboard and click-to-jump controls, and local conversation search.", + "zh": "左侧用户轮次导航轨道:随阅读位置高亮,悬停预览单轮指标与模型回答摘要,支持键盘和点击跳转及本地会话搜索。" + }, + "npm": "dsh-codex-timeline", + "stars": 9, + "downloads": 2664, + "install": "dsh plugin --profile web add dsh-codex-timeline", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/Wine-Red/dsh-codex-timeline/HEAD/docs/images/timeline-hover-dsh-dark.png", + "https://raw.githubusercontent.com/Wine-Red/dsh-codex-timeline/HEAD/docs/images/timeline-hover-dsh.png" + ] + }, + { + "name": "dsh-deep-verbs", + "owner": "Winter-And-You-Gone", + "url": "https://github.com/Winter-And-You-Gone/dsh-deep-verbs", + "page": "https://awesome-dsh-plugin.com/p/Winter-And-You-Gone/dsh-deep-verbs/", + "category": "ui", + "description": { + "en": "Rotates the model status line through a 53-phrase English/Chinese deep-theme pool: a random pick at turn start, event-driven switching on new thinking segments or tool calls (3s throttle), and a click on the status line toggles the language.", + "zh": "把内置 'Deep diving...' 思考状态行扩展为 53 条 deep 系英/中双语短语池:回合开场随机一条,随新思考段/工具调用事件轮换(最短间隔 3 秒),点击状态行切换中英文。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Winter-And-You-Gone/dsh-deep-verbs", + "added": "2026-08-19" + }, + { + "name": "dsh-turn-fold", + "owner": "Winter-And-You-Gone", + "url": "https://github.com/Winter-And-You-Gone/dsh-turn-fold", + "page": "https://awesome-dsh-plugin.com/p/Winter-And-You-Gone/dsh-turn-fold/", + "category": "ui", + "description": { + "en": "Automatically folds tool calls and Think segments into collapsible groups, then collapses each finished turn into a single header showing live duration, token consumption, throughput, and cache-hit metrics.", + "zh": "自动折叠工具调用与 Think 段,回合结束后整回合收成一个大组头,实时显示耗时、token 消耗、tok/s 及缓存命中率等指标。" + }, + "npm": "dsh-turn-fold", + "stars": 6, + "downloads": 400, + "install": "dsh plugin --profile web add dsh-turn-fold", + "added": "2026-08-18" + }, + { + "name": "dsh-web-preview-float", + "owner": "WJNCT55555", + "url": "https://github.com/WJNCT55555/dsh-web-preview-float", + "page": "https://awesome-dsh-plugin.com/p/WJNCT55555/dsh-web-preview-float/", + "category": "ui", + "description": { + "en": "Google AI Studio-style floating preview for the Web UI: a draggable/resizable iframe preview with aspect-ratio and resolution presets, plus a VS Code-style code window with direct editing, confirmed writes, and session-log recording.", + "zh": "Web UI 悬浮预览与代码窗口:Google AI Studio 风格的 iframe 预览(宽幅比/分辨率预设、一键放大),VS Code 风格代码窗支持直接编辑,保存需确认并写入会话日志。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:WJNCT55555/dsh-web-preview-float", + "added": "2026-08-16" + }, + { + "name": "dsh-web-default-session", + "owner": "wjy9902", + "url": "https://github.com/wjy9902/dsh-web-default-session", + "page": "https://awesome-dsh-plugin.com/p/wjy9902/dsh-web-default-session/", + "category": "ui", + "description": { + "en": "The generic New Session action opens a default-directory workspace instead of requiring a folder pick, and the workspace picker lists that workspace as a no-folder choice.", + "zh": "点「新会话」默认打开绑定宿主启动目录的「默认目录」工作区(无需选文件夹),工作区选择菜单中也可选该项。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:wjy9902/dsh-web-default-session", + "added": "2026-08-14" + }, + { + "name": "dsh-attachments", + "owner": "WJZ-P", + "url": "https://github.com/WJZ-P/dsh-attachments", + "page": "https://awesome-dsh-plugin.com/p/WJZ-P/dsh-attachments/", + "category": "ui", + "description": { + "en": "Adds file and folder drag-and-drop attachments to the Web UI, with composer cards, persistent history, and workspace-backed paths.", + "zh": "为 Web UI 添加文件与文件夹拖放附件,提供输入栏卡片、持久化历史记录与工作区路径。" + }, + "npm": "dsh-attachment", + "stars": 5, + "downloads": 348, + "install": "dsh plugin --profile web add dsh-attachment", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/WJZ-P/dsh-attachments/main/assets/markdown/01-drag-drop-overlay.png", + "https://raw.githubusercontent.com/WJZ-P/dsh-attachments/main/assets/markdown/02-image-conversation.png", + "https://raw.githubusercontent.com/WJZ-P/dsh-attachments/main/assets/markdown/03-multiple-image-preview.png" + ] + }, + { + "name": "dsh-ui-tweaks", + "owner": "wlj521", + "url": "https://github.com/wlj521/dsh-ui-tweaks", + "page": "https://awesome-dsh-plugin.com/p/wlj521/dsh-ui-tweaks/", + "category": "ui", + "description": { + "en": "Live-tune the DSH Web conversation UI from Settings: message font size, Claude Desktop-style markdown tables, dialog width, an optional timeline rail, and a GitBar with branch management, per-file diff, and commit & push.", + "zh": "从设置面板实时调教 DSH 网页对话界面:消息字体大小、Claude Desktop 风格 Markdown 表格、对话框宽度、可选时间线侧栏,以及带分支管理、逐文件 Diff 和提交推送功能的 GitBar。" + }, + "npm": "dsh-ui-tweaks", + "stars": 12, + "downloads": 3586, + "install": "dsh plugin --profile web add dsh-ui-tweaks", + "added": "2026-08-17" + }, + { + "name": "dsh-prompt-optimizer", + "owner": "wmengxiang", + "url": "https://github.com/wmengxiang/dsh-prompt-optimizer", + "page": "https://awesome-dsh-plugin.com/p/wmengxiang/dsh-prompt-optimizer/", + "category": "ui", + "description": { + "en": "Adds an icon button to the composer that optimizes the current draft prompt through the active model.", + "zh": "在输入框新增图标按钮,一键调用当前模型把提示词优化得更清晰具体。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:wmengxiang/dsh-prompt-optimizer", + "added": "2026-08-20" + }, + { + "name": "dsh-desktop-tray", + "owner": "wodongx123", + "url": "https://github.com/wodongx123/dsh-desktop-tray", + "page": "https://awesome-dsh-plugin.com/p/wodongx123/dsh-desktop-tray/", + "category": "ui", + "description": { + "en": "Adds a system tray icon to DSH Desktop, with settings to hide the main window to the tray on minimize or close and self-healing after app updates.", + "zh": "为 DSH Desktop 增加系统托盘图标,可在设置中选择最小化或关闭时隐藏到托盘,应用更新后自动恢复。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:wodongx123/dsh-desktop-tray", + "added": "2026-08-19" + }, + { + "name": "dsh-ds-attach", + "owner": "wqx-txdsyl", + "url": "https://github.com/wqx-txdsyl/dsh-ds-attach", + "page": "https://awesome-dsh-plugin.com/p/wqx-txdsyl/dsh-ds-attach/", + "category": "ui", + "description": { + "en": "**DeepSeek Chat (chat.deepseek.com) same-style attachments**: paperclip upload button, 240×64 DS-style file cards with official colored doc icons (PDF/DOCX/XLSX/PPT/IMG extracted from the DS frontend bundle), drag-and-drop, text extraction (PDF/DOCX/XLSX/TXT) injected into the message, and real attachment cards rendered in the transcript (custom user renderer) with the model reading full content.", + "zh": "**DeepSeek Chat(chat.deepseek.com)同款附件**:回形针上传按钮、240×64 DS 风格文件卡片(官方彩色文档图标,从 DS 前端 bundle 提取)、拖拽上传、文本提取(PDF/DOCX/XLSX/TXT)注入消息、对话流中渲染真实附件卡片(自定义 user 渲染器)且模型读取完整内容。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:wqx-txdsyl/dsh-ds-attach", + "added": "2026-08-16" + }, + { + "name": "dsh-reasoning-slider", + "owner": "WSL043", + "url": "https://github.com/WSL043/dsh-reasoning-slider", + "page": "https://awesome-dsh-plugin.com/p/WSL043/dsh-reasoning-slider/", + "category": "ui", + "description": { + "en": "Adds a model-aware reasoning effort slider with native and animated modes, light and dark palettes, and shared or per-model colors.", + "zh": "为模型添加推理强度滑块,支持原生与动画模式、明暗配色,以及全局或按模型配色。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:WSL043/dsh-reasoning-slider", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/WSL043/dsh-reasoning-slider/main/docs/assets/reasoning-slider-en.png", + "https://raw.githubusercontent.com/WSL043/dsh-reasoning-slider/main/docs/assets/reasoning-slider-energy-zh.png", + "https://raw.githubusercontent.com/WSL043/dsh-reasoning-slider/main/docs/assets/mode-settings-en.png", + "https://raw.githubusercontent.com/WSL043/dsh-reasoning-slider/main/docs/assets/mode-settings-dark-zh.png" + ] + }, + { + "name": "dsh-plugins#dsh-composer-tools", + "owner": "wsxwj123", + "url": "https://github.com/wsxwj123/dsh-plugins/tree/main/packages/dsh-composer-tools", + "page": "https://awesome-dsh-plugin.com/p/wsxwj123/dsh-plugins--packages-dsh-composer-tools/", + "category": "ui", + "description": { + "en": "Composer input toolkit: arrow-key session history (first/last-line gated, IME and command-menu safe) and related input conveniences.", + "zh": "输入框工具集:方向键调取历史消息(限首/末行触发,兼容输入法与命令菜单)等输入增强。" + }, + "npm": "dsh-composer-tools", + "stars": 8, + "downloads": 142, + "install": "dsh plugin --profile web add dsh-composer-tools", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/wsxwj123/dsh-plugins/main/assets/screenshots/composer-tools.png" + ] + }, + { + "name": "dsh-plugins#dsh-turn-scrubber", + "owner": "wsxwj123", + "url": "https://github.com/wsxwj123/dsh-plugins/tree/main/packages/dsh-turn-scrubber", + "page": "https://awesome-dsh-plugin.com/p/wsxwj123/dsh-plugins--packages-dsh-turn-scrubber/", + "category": "ui", + "description": { + "en": "Compact right-edge turn rail with hover summaries and click-to-jump navigation.", + "zh": "右侧紧凑回合刻度条,悬停显示回合摘要,点击跳转到对应用户回合。" + }, + "npm": "dsh-turn-scrubber", + "stars": 8, + "downloads": 135, + "install": "dsh plugin --profile web add dsh-turn-scrubber", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/wsxwj123/dsh-plugins/main/assets/screenshots/turn-scrubber.png" + ] + }, + { + "name": "dsh-phone#client", + "owner": "wwumit", + "url": "https://github.com/wwumit/dsh-phone/tree/main/client", + "page": "https://awesome-dsh-plugin.com/p/wwumit/dsh-phone--client/", + "category": "ui", + "description": { + "en": "iPhone-style agent phone: dual-panel calls & SMS, RCS group chat with trust gates, cross-device agent messaging via the registry inbox bridge, L0–L4 trust badges and evidence audit. Experimental: trust summaries are not a security guarantee, and SMS/signalling is relayed through the operator's inbox (visible to them); E2E encryption is planned.", + "zh": "苹果风格智能体手机:双面板电话/短信、RCS 群聊(信任门禁)、跨设备智能体消息(registry 收件箱桥)、L0–L4 信任徽章与证据审计。实验性:信任摘要非安全保证,短信/信令经运营方收件箱中继(运营方可见);E2E 加密为演进方向。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:wwumit/dsh-phone#path:/client", + "added": "2026-08-28" + }, + { + "name": "dsh-message-rail", + "owner": "wx-yss", + "url": "https://github.com/wx-yss/dsh-message-rail", + "page": "https://awesome-dsh-plugin.com/p/wx-yss/dsh-message-rail/", + "category": "ui", + "description": { + "en": "Codex-style left-side message navigation rail for the Web UI: one tick per user message, hover previews, and click-to-jump across the whole history.", + "zh": "Codex 风格左侧消息导航轨道:每条用户消息一个刻度,悬停预览、点击跳转,全历史一次索引。" + }, + "npm": "dsh-message-rail", + "stars": 15, + "downloads": 1723, + "install": "dsh plugin --profile web add dsh-message-rail", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/wx-yss/dsh-message-rail/HEAD/assets/rail-hover-preview.png" + ] + }, + { + "name": "dsh-hover-approve", + "owner": "wydddddcool", + "url": "https://github.com/wydddddcool/dsh-hover-approve", + "page": "https://awesome-dsh-plugin.com/p/wydddddcool/dsh-hover-approve/", + "category": "ui", + "description": { + "en": "Anchors an approve/answer bubble to each DSH Web sidebar session row — one-click handling of authorization, questions, plan reviews and blocked goals without opening the conversation.", + "zh": "DSH Web 侧边栏锚定气泡:会话待授权、提问、计划确认、目标阻断时在会话行旁自动弹出并一键处理,无需点进会话。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:wydddddcool/dsh-hover-approve", + "added": "2026-08-19" + }, + { + "name": "dsh-rule-manager", + "owner": "wzz3034026545", + "url": "https://github.com/wzz3034026545/dsh-rule-manager", + "page": "https://awesome-dsh-plugin.com/p/wzz3034026545/dsh-rule-manager/", + "category": "ui", + "description": { + "en": "Manage DSH rules from the web settings panel — edit global/project AGENTS.md, or auto-split pasted rules into layered AGENTS.md and skills via LLM.", + "zh": "在设置面板统一管理 DSH 规则:编辑全局/项目 AGENTS.md,或由 LLM 自动拆分粘贴的规则为分层 AGENTS.md 与技能。" + }, + "npm": "dsh-rule-manager", + "stars": 1, + "downloads": 237, + "install": "dsh plugin --profile web add dsh-rule-manager", + "added": "2026-08-18" + }, + { + "name": "dsh-client-ui-writing", + "owner": "x2802490130-prog", + "url": "https://github.com/x2802490130-prog/dsh-client-ui-writing", + "page": "https://awesome-dsh-plugin.com/p/x2802490130-prog/dsh-client-ui-writing/", + "category": "ui", + "description": { + "en": "Client-side writing panel for the Web UI: project volumes and stats, corpus library, full-text search, evolution version-chain diffs, and an SVG thread graph, shown only in writing-preset sessions.", + "zh": "Web 客户端「写作」面板:项目分卷与统计、书库与全文检索、设定演化版本链 diff、线索 SVG 图谱,仅在写作预设会话显示。" + }, + "npm": "dsh-client-ui-writing", + "stars": 4, + "downloads": 1106, + "install": "dsh plugin --profile web add dsh-client-ui-writing", + "added": "2026-08-15" + }, + { + "name": "dsh-mpkg-wallpaper", + "owner": "XHR666", + "url": "https://github.com/XHR666/dsh-mpkg-wallpaper", + "page": "https://awesome-dsh-plugin.com/p/XHR666/dsh-mpkg-wallpaper/", + "category": "ui", + "description": { + "en": "Load Wallpaper Engine .mpkg files as the DSH web background: embedded video, time-of-day switching, unified frosted blur, local wallpaper library & rotation.", + "zh": "在浏览器里直接加载 Wallpaper Engine 的 .mpkg 作为 DSH 网页背景:内嵌视频、多时段切换、统一磨砂虚化、本地壁纸库与轮换。" + }, + "npm": "dsh-mpkg-wallpaper", + "stars": 6, + "downloads": 2751, + "install": "dsh plugin --profile web add dsh-mpkg-wallpaper", + "added": "2026-08-18" + }, + { + "name": "OpenQuantum#openquantum-web-branding", + "owner": "xi-zhao", + "url": "https://github.com/xi-zhao/OpenQuantum/tree/main/packages/openquantum-web-branding", + "page": "https://awesome-dsh-plugin.com/p/xi-zhao/OpenQuantum--packages-openquantum-web-branding/", + "category": "ui", + "description": { + "en": "Applies OpenQuantum branding, browser metadata, and PWA assets to the native DeepSeek Harness web UI.", + "zh": "为 DeepSeek Harness 原生 Web 界面应用 OpenQuantum 品牌、浏览器元数据与 PWA 资产。" + }, + "npm": null, + "stars": 47, + "downloads": null, + "install": "dsh plugin --profile web add github:xi-zhao/OpenQuantum#path:/packages/openquantum-web-branding", + "added": "2026-08-23" + }, + { + "name": "dsh-turn-nav", + "owner": "xiaheng1", + "url": "https://github.com/xiaheng1/dsh-turn-nav", + "page": "https://awesome-dsh-plugin.com/p/xiaheng1/dsh-turn-nav/", + "category": "ui", + "description": { + "en": "Turn histogram navigation rail for DeepSeek Harness Web with mixed, DeepSeek, and Codex visual variants.", + "zh": "DeepSeek Harness Web 对话轮次导航轨,支持 mixed / DeepSeek / Codex 三种视觉风格。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:xiaheng1/dsh-turn-nav", + "added": "2026-08-19" + }, + { + "name": "dsh-quick-phrases", + "owner": "xianrui69", + "url": "https://github.com/xianrui69/dsh-quick-phrases", + "page": "https://awesome-dsh-plugin.com/p/xianrui69/dsh-quick-phrases/", + "category": "ui", + "description": { + "en": "Adds a draggable, position-persisted chip bar of configurable quick phrases to DeepSeek Harness, plus a /-triggered phrase menu with per-phrase pin, slash-menu, bar-visibility, and click-to-send toggles.", + "zh": "为 DeepSeek Harness 增加可拖拽、位置会记住的快捷短语条,以及 `/` 触发的短语菜单;每条短语可单独设置置顶、斜杠菜单、是否上条和点击即发送。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:xianrui69/dsh-quick-phrases", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/xianrui69/dsh-quick-phrases/HEAD/docs/flaw-2-menu-after-arrow-keys.png", + "https://raw.githubusercontent.com/xianrui69/dsh-quick-phrases/HEAD/docs/flaw-2-menu-default-view.png", + "https://raw.githubusercontent.com/xianrui69/dsh-quick-phrases/HEAD/docs/flaw-1-alignment.png" + ] + }, + { + "name": "dsh-prompt-history", + "owner": "Xiaofei-fei", + "url": "https://github.com/Xiaofei-fei/dsh-prompt-history", + "page": "https://awesome-dsh-plugin.com/p/Xiaofei-fei/dsh-prompt-history/", + "category": "ui", + "description": { + "en": "Terminal-style copy & paste plus bash-like prompt history for the DSH web composer: any selection auto-copies, right-click pastes the clipboard directly, and Up/Down arrows recall previously submitted messages — history sourced from the session's own message log (zero config, zero extra storage).", + "zh": "为 DSH Web 输入框提供终端式复制粘贴与类 Linux shell 的提示词历史:选中即自动复制、右键直接粘贴、↑/↓ 召回之前发送过的消息,历史取自会话自身的消息记录(零配置、零额外存储)。" + }, + "npm": "dsh-prompt-history", + "stars": 1, + "downloads": 1149, + "install": "dsh plugin --profile web add dsh-prompt-history", + "added": "2026-08-20" + }, + { + "name": "dsh-tui", + "owner": "xiaoshihou514", + "url": "https://github.com/xiaoshihou514/dsh-tui", + "page": "https://awesome-dsh-plugin.com/p/xiaoshihou514/dsh-tui/", + "category": "ui", + "description": { + "en": "Clean and simple TUI.", + "zh": "干净简单的终端交互式界面。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:xiaoshihou514/dsh-tui", + "added": "2026-08-16" + }, + { + "name": "dsh-skin-manager", + "owner": "xiaoyangcheng84-svg", + "url": "https://github.com/xiaoyangcheng84-svg/dsh-skin-manager", + "page": "https://awesome-dsh-plugin.com/p/xiaoyangcheng84-svg/dsh-skin-manager/", + "category": "ui", + "description": { + "en": "Skin manager for the DSH web GUI: discovers installed skins and switches the active one from a dedicated Settings page, mutually exclusive, hot-reloaded without restart.", + "zh": "DSH Web GUI 皮肤管理器:自动发现已安装皮肤,在独立设置页一键互斥切换,热载入无需重启。" + }, + "npm": "dsh-skin-manager", + "stars": 6, + "downloads": 886, + "install": "dsh plugin --profile web add dsh-skin-manager", + "added": "2026-08-15" + }, + { + "name": "dsh-showreel", + "owner": "xiaoyuer3921", + "url": "https://github.com/xiaoyuer3921/dsh-showreel", + "page": "https://awesome-dsh-plugin.com/p/xiaoyuer3921/dsh-showreel/", + "category": "ui", + "description": { + "en": "Turns the latest completed DSH session turn into a shareable vertical recap video: editable five-scene storyboard, host-side privacy redaction, optional AI polish and TTS narration, MP4/WebM export with PNG cover.", + "zh": "把最近一次已完成的 DSH 任务生成为可分享的竖屏战报视频:五幕可编辑故事板、Host 侧脱敏、可选 AI 润色与配音,导出 MP4/WebM 和 PNG 封面。" + }, + "npm": "dsh-showreel", + "stars": 2, + "downloads": 449, + "install": "dsh plugin --profile web add dsh-showreel", + "added": "2026-08-20" + }, + { + "name": "dsh-session-timeline", + "owner": "XiLuovo", + "url": "https://github.com/XiLuovo/dsh-session-timeline", + "page": "https://awesome-dsh-plugin.com/p/XiLuovo/dsh-session-timeline/", + "category": "ui", + "description": { + "en": "Session timeline down the left of the conversation: every user message at a glance, current-message tracking, hover preview of the message and its reply, click to jump, collapsible.", + "zh": "会话左侧的短横线时间轴:整个会话的用户消息一览、当前消息定位、悬停预览消息与回复、点击跳转,可收起展开。" + }, + "npm": "dsh-session-timeline", + "stars": 4, + "downloads": 586, + "install": "dsh plugin --profile web add dsh-session-timeline", + "added": "2026-08-15" + }, + { + "name": "dsh-ui-tools", + "owner": "xing-shuyin", + "url": "https://github.com/xing-shuyin/dsh-ui-tools", + "page": "https://awesome-dsh-plugin.com/p/xing-shuyin/dsh-ui-tools/", + "category": "ui", + "description": { + "en": "Developer tools panel for the DSH web UI: file browser with preview, edit, and path-only file mentions; multi-tab terminals with quick commands; Git panel with commit, push, pull, and branch switching; and background job management. UI ported from pi-web-ui.", + "zh": "dsh Web UI 开发者工具面板:文件浏览/预览/编辑与路径式文件提及、多终端(快捷命令)、Git 面板(提交/推送/拉取/分支)与后台任务管理。UI 移植自 pi-web-ui。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:xing-shuyin/dsh-ui-tools", + "added": "2026-08-19" + }, + { + "name": "dsh-zh-labels", + "owner": "Xingkong42", + "url": "https://github.com/Xingkong42/dsh-zh-labels", + "page": "https://awesome-dsh-plugin.com/p/Xingkong42/dsh-zh-labels/", + "category": "ui", + "description": { + "en": "Re-applies Chinese UI labels to the DSH client modules on every start, so translations survive upgrades.", + "zh": "每次启动自动将 DSH 界面英文工具与状态标签修补为中文,升级 DSH 后自动恢复,无需手动改文件。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Xingkong42/dsh-zh-labels", + "added": "2026-08-23" + }, + { + "name": "dsh-approval-translator", + "owner": "xiyuepcl", + "url": "https://github.com/xiyuepcl/dsh-approval-translator", + "page": "https://awesome-dsh-plugin.com/p/xiyuepcl/dsh-approval-translator/", + "category": "ui", + "description": { + "en": "DSH审批汉化 — approval dialogs translated to Chinese before they render: DeepSeek-powered, host-side, nothing for React to revert.", + "zh": "DSH审批汉化 — 审批弹窗在渲染前自动翻译成中文:DeepSeek 驱动、宿主侧完成,React 重渲染也无法还原。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:xiyuepcl/dsh-approval-translator", + "added": "2026-08-18" + }, + { + "name": "dsh-session-theme", + "owner": "Xliecc", + "url": "https://github.com/Xliecc/dsh-session-theme", + "page": "https://awesome-dsh-plugin.com/p/Xliecc/dsh-session-theme/", + "category": "ui", + "description": { + "en": "Shows every session theme in the DSH Web left sidebar immediately on page load by warming the session projection cache at startup; no need to click into a conversation first.", + "zh": "打开页面时左侧边栏直接显示每个会话的主题,无需点进会话。启动时预热会话投影缓存,让冷会话也能显示真实标题。" + }, + "npm": "dsh-session-theme", + "stars": 3, + "downloads": 317, + "install": "dsh plugin --profile web add dsh-session-theme", + "added": "2026-08-21" + }, + { + "name": "dsh-pi-tui", + "owner": "XMoon", + "url": "https://github.com/XMoon/dsh-pi-tui", + "page": "https://awesome-dsh-plugin.com/p/XMoon/dsh-pi-tui/", + "category": "ui", + "description": { + "en": "Third-party TUI mode for DeepSeek Harness (dsh --profile pi-tui), built on a vendored pi-tui fork: covers the main session loop plus approvals, commands, session switching and full-text search, as well as presets, skills, model/settings menus and slash commands.", + "zh": "DeepSeek Harness 的第三方 TUI 模式(dsh --profile pi-tui),基于内置 pi-tui fork 构建:覆盖主会话循环(审批、命令、会话切换与全文搜索),以及预设、技能、模型/设置菜单与斜杠命令。" + }, + "npm": "@xmoon76/dsh-pi-tui", + "stars": 12, + "downloads": 2925, + "install": "dsh plugin --profile web add @xmoon76/dsh-pi-tui", + "added": "2026-08-23" + }, + { + "name": "dsh-prompt-optimizer", + "owner": "Y1X1n", + "url": "https://github.com/Y1X1n/dsh-prompt-optimizer", + "page": "https://awesome-dsh-plugin.com/p/Y1X1n/dsh-prompt-optimizer/", + "category": "ui", + "description": { + "en": "Adds an optimize button beside the composer that analyzes and rewrites the prompt draft with SSE streaming, conversation-context awareness (template vs intent-polish strategies), an edit-continuity memory chain, undo, and model routing with fallback and a connectivity test.", + "zh": "在输入框旁加「优化」按钮:流式分析并改写提示词草稿,支持会话上下文感知(模板/润色双策略)、跨编辑记忆链与撤回,模型路由带 fallback 与连通性测试。" + }, + "npm": null, + "tarball": "https://github.com/Y1X1n/dsh-prompt-optimizer/releases/latest/download/dsh-prompt-optimizer.tgz", + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/Y1X1n/dsh-prompt-optimizer/releases/latest/download/dsh-prompt-optimizer.tgz\"", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/Y1X1n/dsh-prompt-optimizer/main/docs/screenshots/optimize-panel.png" + ] + }, + { + "name": "dsh-knj-menu", + "owner": "yangdongzhen590", + "url": "https://github.com/yangdongzhen590/dsh-knj-menu", + "page": "https://awesome-dsh-plugin.com/p/yangdongzhen590/dsh-knj-menu/", + "category": "ui", + "description": { + "en": "Third-party menu manager for the DSH Web UI: collects new menu entries added by other plugins into one place, with fold/collapse and pinned menu items.", + "zh": "第三方菜单管理器:统一收纳第三方插件的新增菜单入口,提供常规折叠与固定菜单项。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:yangdongzhen590/dsh-knj-menu", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/yangdongzhen590/dsh-knj-menu/main/assets/screenshots/menu-manager.png" + ] + }, + { + "name": "dsh-editor", + "owner": "yangshen830-eng", + "url": "https://github.com/yangshen830-eng/dsh-editor", + "page": "https://awesome-dsh-plugin.com/p/yangshen830-eng/dsh-editor/", + "category": "ui", + "description": { + "en": "VS Code-style code editor: sidebar file tree over the workspace, Monaco editing, file read/write, Git status and diff, search, and Markdown preview.", + "zh": "VS Code 风格代码编辑器:侧边栏文件树浏览工作区,Monaco 编辑,文件读写,Git 状态与差异,搜索与 Markdown 预览。" + }, + "npm": "dsh-editor", + "stars": 1, + "downloads": 576, + "install": "dsh plugin --profile web add dsh-editor", + "added": "2026-08-19" + }, + { + "name": "dsh-about", + "owner": "YannZhou", + "url": "https://github.com/YannZhou/dsh-about", + "page": "https://awesome-dsh-plugin.com/p/YannZhou/dsh-about/", + "category": "ui", + "description": { + "en": "About section for the DeepSeek Harness settings: version info, npm latest/next update check with one-click install and auto-restart, and GitHub release history.", + "zh": "在 DeepSeek Harness 设置中心增加「关于」分区:版本信息、npm 检查更新与一键安装(自动重启)、GitHub 版本更新记录。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:YannZhou/dsh-about", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/YannZhou/dsh-about/HEAD/assets/dsh-about.png" + ] + }, + { + "name": "dsh-result-only-view", + "owner": "YEYEYEYESHIFU", + "url": "https://github.com/YEYEYEYESHIFU/dsh-result-only-view", + "page": "https://awesome-dsh-plugin.com/p/YEYEYEYESHIFU/dsh-result-only-view/", + "category": "ui", + "description": { + "en": "Results-only conversation toggle for the Web GUI: hides thinking and tool-call process rows, keeps one live status line while the agent works, restores working animations under prefers-reduced-motion, zh-CN/en localized.", + "zh": "Web 对话「只看结果」开关:隐藏思考与工具调用过程,运行中仅保留一条实时状态行,reduced-motion 环境下恢复活动光影,中英双语。" + }, + "npm": "dsh-result-only-view", + "stars": 2, + "downloads": 864, + "install": "dsh plugin --profile web add dsh-result-only-view", + "added": "2026-08-16" + }, + { + "name": "dsh-mobile-glass", + "owner": "YiRan0", + "url": "https://github.com/YiRan0/dsh-mobile-glass", + "page": "https://awesome-dsh-plugin.com/p/YiRan0/dsh-mobile-glass/", + "category": "ui", + "description": { + "en": "Mobile adaptation for the DSH Web UI on narrow screens: reveal drawer with the chat layer on top, drag gestures, bottom-sheet settings panel, composer and header fixes, zero desktop impact.", + "zh": "DSH Web 移动端适配:窄屏下聊天页在上/侧栏在下的 reveal 抽屉、拖动手势、设置面板底部卡片上滑、composer 与头部修复,桌面端零影响。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:YiRan0/dsh-mobile-glass", + "added": "2026-08-16" + }, + { + "name": "dsh-open-explorer", + "owner": "YooRarely", + "url": "https://github.com/YooRarely/dsh-open-explorer", + "page": "https://awesome-dsh-plugin.com/p/YooRarely/dsh-open-explorer/", + "category": "ui", + "description": { + "en": "Adds buttons to open the current DSH workspace root in the host file manager.", + "zh": "在 DSH 会话标题和工作区菜单中添加按钮,用系统文件管理器打开当前 Workspace 根目录。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:YooRarely/dsh-open-explorer", + "added": "2026-08-18" + }, + { + "name": "dsh-pet", + "owner": "ysyyhhh", + "url": "https://github.com/ysyyhhh/dsh-pet", + "page": "https://awesome-dsh-plugin.com/p/ysyyhhh/dsh-pet/", + "category": "ui", + "description": { + "en": "Native desktop pet for DSH that follows agent activity, supports Codex pet packages, and imports approved pets directly from Petdex without its CLI.", + "zh": "跟随 agent 状态的 DSH 原生桌宠,兼容 Codex 桌宠包,并可在插件内直接从 Petdex 导入已审核桌宠,无需 Petdex CLI。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:ysyyhhh/dsh-pet", + "added": "2026-08-15" + }, + { + "name": "dsh-read-image-view", + "owner": "Yu-tao-Li", + "url": "https://github.com/Yu-tao-Li/dsh-read-image-view", + "page": "https://awesome-dsh-plugin.com/p/Yu-tao-Li/dsh-read-image-view/", + "category": "ui", + "description": { + "en": "Displays the images read in the conversation (read_image results) in the DSH Web GUI: a dedicated Read image row with a default thumbnail and an in-page full-resolution lightbox (zoom buttons, mouse wheel, 1:1 original size), plus the metadata envelope.", + "zh": "用于展示对话中读取的图片(read_image 的结果):在 DeepSeek Harness Web GUI 对话流中以专用 Read image 行呈现,默认缩略图 + 页面内全精度放大层(缩放按钮/滚轮/1:1 原始尺寸)+ 元数据信封。" + }, + "npm": "dsh-read-image-view", + "stars": 5, + "downloads": 176, + "install": "dsh plugin --profile web add dsh-read-image-view", + "added": "2026-08-20", + "screenshots": [ + "https://raw.githubusercontent.com/Yu-tao-Li/dsh-read-image-view/main/assets/screenshot-1.png", + "https://raw.githubusercontent.com/Yu-tao-Li/dsh-read-image-view/main/assets/screenshot-2.png", + "https://raw.githubusercontent.com/Yu-tao-Li/dsh-read-image-view/main/assets/screenshot-3.png", + "https://raw.githubusercontent.com/Yu-tao-Li/dsh-read-image-view/main/assets/screenshot-4.png" + ] + }, + { + "name": "dsh-free-models-hub", + "owner": "yu-wenchao", + "url": "https://github.com/yu-wenchao/dsh-free-models-hub", + "page": "https://awesome-dsh-plugin.com/p/yu-wenchao/dsh-free-models-hub/", + "category": "ui", + "description": { + "en": "A drawer panel for the DSH web UI with a paginated list of free LLMs; one-click or batch writes each as a custom model provider into Settings, plus a local multi-key rotation proxy.", + "zh": "DSH Web UI 右侧的免费模型抽屉面板,分页浏览免费模型,可一键或整页批量把模型写入自定义提供方,并附带本地多 Key 轮换代理。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:yu-wenchao/dsh-free-models-hub", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/yu-wenchao/dsh-free-models-hub/HEAD/screenshots/free1.png", + "https://raw.githubusercontent.com/yu-wenchao/dsh-free-models-hub/HEAD/screenshots/free2.png", + "https://raw.githubusercontent.com/yu-wenchao/dsh-free-models-hub/HEAD/screenshots/free3.png", + "https://raw.githubusercontent.com/yu-wenchao/dsh-free-models-hub/HEAD/screenshots/free4.png" + ] + }, + { + "name": "dsh-decision-split", + "owner": "yuanbaoerer", + "url": "https://github.com/yuanbaoerer/dsh-decision-split", + "page": "https://awesome-dsh-plugin.com/p/yuanbaoerer/dsh-decision-split/", + "category": "ui", + "description": { + "en": "Decision split view for DSH web UI: a decision summary card in the main window plus the full plan in an independently scrollable side panel, so you can review the whole plan while writing decisions and send them back to the agent with one click.", + "zh": "决策分屏视图:主窗口显示决策摘要卡片,右侧详情栏独立滚动展示完整方案,边看全文边写决策,一键回传 agent。" + }, + "npm": "dsh-decision-split", + "stars": 3, + "downloads": 1278, + "install": "dsh plugin --profile web add dsh-decision-split", + "added": "2026-08-22" + }, + { + "name": "dsh-mobile-ui", + "owner": "yuanzhenqi", + "url": "https://github.com/yuanzhenqi/dsh-mobile-ui", + "page": "https://awesome-dsh-plugin.com/p/yuanzhenqi/dsh-mobile-ui/", + "category": "ui", + "description": { + "en": "Phone-first drawer shell for DSH Web. Collapses the session list into a tap-to-open drawer, keeps header plugin actions, and turns Settings into a full-width sheet.", + "zh": "为 DSH Web 做成真正的手机壳:会话列表收成抽屉,点会话自动收起且不弹键盘,设置页改为全宽 sheet。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:yuanzhenqi/dsh-mobile-ui", + "added": "2026-08-28" + }, + { + "name": "NoLetMe", + "owner": "Yuer6327", + "url": "https://github.com/Yuer6327/NoLetMe", + "page": "https://awesome-dsh-plugin.com/p/Yuer6327/NoLetMe/", + "category": "ui", + "description": { + "en": "Real-time 'We need…' and 'Let me…' reasoning-trajectory keyword stats for the DeepSeek Harness web chat.", + "zh": "实时追踪 DeepSeek Harness 网页对话中模型流式推理轨迹关键词统计的 UI 面板。" + }, + "npm": "dsh-noletme", + "stars": 8, + "downloads": 1430, + "install": "dsh plugin --profile web add dsh-noletme", + "added": "2026-08-18" + }, + { + "name": "dsh-turn-rail", + "owner": "Yujm888", + "url": "https://github.com/Yujm888/dsh-turn-rail", + "page": "https://awesome-dsh-plugin.com/p/Yujm888/dsh-turn-rail/", + "category": "ui", + "description": { + "en": "Codex-style auto-hiding right-edge turn rail: edge-peek show/hide, per-turn tooltip, in-rail search, and a scrolling 30-turn window for long sessions.", + "zh": "Codex 风格自动隐藏右缘轮次导航:靠边浮现、单轮 tooltip、会话内搜索、长会话 30 轮滚动窗口。" + }, + "npm": "dsh-turn-rail", + "stars": 3, + "downloads": 208, + "install": "dsh plugin --profile web add dsh-turn-rail", + "added": "2026-08-15" + }, + { + "name": "dsh-reveal-files", + "owner": "yumm007", + "url": "https://github.com/yumm007/dsh-reveal-files", + "page": "https://awesome-dsh-plugin.com/p/yumm007/dsh-reveal-files/", + "category": "ui", + "description": { + "en": "Turns each produced-file chip into a per-file menu: open the file, copy its path, reveal it in the native file browser, or open a terminal cd-ed into its directory.", + "zh": "把每个产物文件卡片变成按文件操作的下拉菜单:打开文件、拷贝路径、在系统文件浏览器中定位,或打开终端并进入其目录。" + }, + "npm": null, + "stars": 0, + "downloads": null, + "install": "dsh plugin --profile web add github:yumm007/dsh-reveal-files", + "added": "2026-08-25" + }, + { + "name": "dsh-workspace-files-explorer", + "owner": "YZz-S", + "url": "https://github.com/YZz-S/dsh-workspace-files-explorer", + "page": "https://awesome-dsh-plugin.com/p/YZz-S/dsh-workspace-files-explorer/", + "category": "ui", + "description": { + "en": "Floating workspace file-tree browser for the Web GUI with line-numbered syntax highlighting and Markdown preview.", + "zh": "悬浮工作区文件树浏览器:带行号的代码语法高亮与 Markdown 富文本预览。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:YZz-S/dsh-workspace-files-explorer", + "added": "2026-08-15" + }, + { + "name": "dsh-side-chat", + "owner": "zclDragon", + "url": "https://github.com/zclDragon/dsh-side-chat", + "page": "https://awesome-dsh-plugin.com/p/zclDragon/dsh-side-chat/", + "category": "ui", + "description": { + "en": "Codex-style /side side conversations for DeepSeek Harness: a temporary fork of the current chat in a floating panel, inheriting context without interrupting the main task.", + "zh": "Codex 风格 /side 侧边对话插件:在当前对话旁开一个浮动分叉面板,继承上下文、不打断主任务。" + }, + "npm": "@zhuchenglong/dsh-side-chat", + "stars": 1, + "downloads": 501, + "install": "dsh plugin --profile web add @zhuchenglong/dsh-side-chat", + "added": "2026-08-24" + }, + { + "name": "dsh-pet", + "owner": "zealot00", + "url": "https://github.com/zealot00/dsh-pet", + "page": "https://awesome-dsh-plugin.com/p/zealot00/dsh-pet/", + "category": "ui", + "description": { + "en": "Desktop pet for the DSH Web UI: sprite-sheet animation, agent state linkage, drag, alarm (daily/one-shot) and pomodoro widgets, skin picker with preview.", + "zh": "DSH Web UI 桌面宠物:精灵图动画、agent 状态联动、拖拽、闹钟(每天/一次)与番茄钟,皮肤下拉选择 + 预览。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:zealot00/dsh-pet", + "added": "2026-08-14" + }, + { + "name": "dsh-any-attachment", + "owner": "Zenjibad", + "url": "https://github.com/Zenjibad/dsh-any-attachment", + "page": "https://awesome-dsh-plugin.com/p/Zenjibad/dsh-any-attachment/", + "category": "ui", + "description": { + "en": "Lets the DSH web UI attach files of any type as pathless @-mentions: pick a workspace file from the @ menu or drop one into the composer, and the agent reads it directly.", + "zh": "让 DSH 网页以无路径 @提及 附加任意类型文件:可从 @ 菜单挑选工作区文件,或拖入输入区,Agent 直接读取。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Zenjibad/dsh-any-attachment", + "added": "2026-08-20" + }, + { + "name": "dsh-mcp-toggle", + "owner": "Zenjibad", + "url": "https://github.com/Zenjibad/dsh-mcp-toggle", + "page": "https://awesome-dsh-plugin.com/p/Zenjibad/dsh-mcp-toggle/", + "category": "ui", + "description": { + "en": "Enables or disables MCP servers from Settings → MCP Servers: stops or starts each @deepseek-ai/dsh-mcp-client connection immediately and persists the change across restarts.", + "zh": "在 DSH 设置 → MCP 服务器中直接启用或停用 MCP:即时停止或启动每个 MCP 客户端连接,并持久化,重启后依旧生效。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Zenjibad/dsh-mcp-toggle", + "added": "2026-08-28" + }, + { + "name": "dsh-settings-nav-organizer", + "owner": "zhengjy01", + "url": "https://github.com/zhengjy01/dsh-settings-nav-organizer", + "page": "https://awesome-dsh-plugin.com/p/zhengjy01/dsh-settings-nav-organizer/", + "category": "ui", + "description": { + "en": "Declutter the DSH settings nav: fold third-party plugin entries under collapsible group rows, with bookmark-style custom groups, a fold toggle, and auto classification (market tags → name rules → your AI model).", + "zh": "设置面板导航整理:第三方插件入口折叠为可展开的分组行,支持书签式自定义分组、折叠开关与自动分类(市场标签 → 名称规则 → AI 模型三级兜底)。" + }, + "npm": "dsh-settings-nav-organizer", + "stars": 2, + "downloads": 3181, + "install": "dsh plugin --profile web add dsh-settings-nav-organizer", + "added": "2026-08-16" + }, + { + "name": "zzy-dsh-prompt-optimizer", + "owner": "zhengzeyong9527-droid", + "url": "https://github.com/zhengzeyong9527-droid/zzy-dsh-prompt-optimizer", + "page": "https://awesome-dsh-plugin.com/p/zhengzeyong9527-droid/zzy-dsh-prompt-optimizer/", + "category": "ui", + "description": { + "en": "Adds prompt optimization, cancellation, and undo controls to the DeepSeek Harness composer.", + "zh": "为 DeepSeek Harness 对话输入框提供提示词优化:优化过程中可取消,完成后可一键撤回。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:zhengzeyong9527-droid/zzy-dsh-prompt-optimizer", + "added": "2026-08-19" + }, + { + "name": "dsh-hover-archive", + "owner": "zhifengjiang", + "url": "https://github.com/zhifengjiang/dsh-hover-archive", + "page": "https://awesome-dsh-plugin.com/p/zhifengjiang/dsh-hover-archive/", + "category": "ui", + "description": { + "en": "Codex-style session archiving: hovering a sidebar conversation row reveals an Archive icon at the far right; one click archives the session via the official workspace archive API.", + "zh": "Codex 风格会话归档:鼠标悬浮侧边栏会话行时,最右侧浮现 Archive 图标,一键归档会话。" + }, + "npm": null, + "stars": 0, + "downloads": null, + "install": "dsh plugin --profile web add github:zhifengjiang/dsh-hover-archive", + "added": "2026-08-25" + }, + { + "name": "dsh-postman", + "owner": "zhousun55-byte", + "url": "https://github.com/zhousun55-byte/dsh-postman", + "page": "https://awesome-dsh-plugin.com/p/zhousun55-byte/dsh-postman/", + "category": "ui", + "description": { + "en": "Upload files and folders straight into the conversation — images attach as real message blocks, text content is written into the composer draft, folders land by directory structure.", + "zh": "把文件与文件夹直接传进对话——图片以真实图片块加入消息,文本内容写入输入框草稿,文件夹按目录结构落盘。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:zhousun55-byte/dsh-postman", + "added": "2026-08-16" + }, + { + "name": "dsh-web#packages/dsh-task-board", + "owner": "zhu1090093659", + "url": "https://github.com/zhu1090093659/dsh-web/tree/main/packages/dsh-task-board", + "page": "https://awesome-dsh-plugin.com/p/zhu1090093659/dsh-web--packages-dsh-task-board/", + "category": "ui", + "description": { + "en": "Task board for the dsh web GUI: a sidebar multi-column kanban whose cards run in real DSH agent sessions and can also be scheduled with cron expressions, executed host-side even with the browser closed.", + "zh": "侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:zhu1090093659/dsh-web#path:/packages/dsh-task-board", + "added": "2026-08-28" + }, + { + "name": "dsh-web#packages/dsh-web-all", + "owner": "zhu1090093659", + "url": "https://github.com/zhu1090093659/dsh-web/tree/main/packages/dsh-web-all", + "page": "https://awesome-dsh-plugin.com/p/zhu1090093659/dsh-web--packages-dsh-web-all/", + "category": "ui", + "description": { + "en": "Plugin and skin collection for the DSH Web UI: task board, Git graph, right-side panel, remote mobile UI, pet, live token stats, and a skin center.", + "zh": "DSH Web UI 插件与皮肤合集:任务看板、git 图、右侧面板、远程移动端 UI、桌宠、实时 token 统计与皮肤中心。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:zhu1090093659/dsh-web#path:/packages/dsh-web-all", + "added": "2026-08-27" + }, + { + "name": "dsh-adaptive-reasoning", + "owner": "zhuifengqug", + "url": "https://github.com/zhuifengqug/dsh-adaptive-reasoning", + "page": "https://awesome-dsh-plugin.com/p/zhuifengqug/dsh-adaptive-reasoning/", + "category": "ui", + "description": { + "en": "Pixel-art reasoning-effort slider for DSH Web with continuous 0–100 dragging, 16ms throttled writes, snap-to-level commits, and a WebGL ember fire that ignites as the slider moves.", + "zh": "炭灰余烬风格的 DSH Web 像素推理强度滑块:以近黑木炭、暗红裂纹和橙红高温核心呈现 4px 像素化 WebGL 火焰,拖动时逐档点燃;支持 0–100 无极拖动、16ms 节流写入与松手吸附。" + }, + "npm": "dsh-adaptive-reasoning", + "stars": 1, + "downloads": 305, + "install": "dsh plugin --profile web add dsh-adaptive-reasoning", + "added": "2026-08-23" + }, + { + "name": "dsh-window#kit", + "owner": "ZichengGurrr", + "url": "https://github.com/ZichengGurrr/dsh-window/tree/main/kit", + "page": "https://awesome-dsh-plugin.com/p/ZichengGurrr/dsh-window--kit/", + "category": "ui", + "description": { + "en": "All-in-one bundle for DSH: the native Windows desktop window (WebView2), DeepEye vision (GLM-4V-Flash), and voice input (mic button) in a single install.", + "zh": "DSH 三件套全家桶:Windows 原生窗口(WebView2)+ DeepEye 视觉(GLM-4V-Flash)+ 语音输入(麦克风按钮),一条命令装齐。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:ZichengGurrr/dsh-window#path:/kit", + "added": "2026-08-15" + }, + { + "name": "dsh-window#plugin", + "owner": "ZichengGurrr", + "url": "https://github.com/ZichengGurrr/dsh-window/tree/main/plugin", + "page": "https://awesome-dsh-plugin.com/p/ZichengGurrr/dsh-window--plugin/", + "category": "ui", + "description": { + "en": "Native Windows desktop window (WebView2) for DSH: one-command install, auto-fetches the app zip from GitHub Releases, creates a desktop shortcut, and adds a desktop_launch tool to start it from chat.", + "zh": "DSH 的 Windows 原生窗口(WebView2):一键安装,自动从 GitHub Releases 下载应用 zip、创建桌面快捷方式,并提供 desktop_launch 工具在对话中一键启动。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:ZichengGurrr/dsh-window#path:/plugin", + "added": "2026-08-14" + }, + { + "name": "dsh-settings-drawer", + "owner": "zizhongfeiyang", + "url": "https://github.com/zizhongfeiyang/dsh-settings-drawer", + "page": "https://awesome-dsh-plugin.com/p/zizhongfeiyang/dsh-settings-drawer/", + "category": "ui", + "description": { + "en": "Lets you choose which items appear in the DeepSeek Harness settings left nav.", + "zh": "让你选择 DeepSeek Harness 设置页左侧导航要显示哪些栏目。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:zizhongfeiyang/dsh-settings-drawer", + "added": "2026-08-19" + }, + { + "name": "dsh-paperclip", + "owner": "zmm863-commits", + "url": "https://github.com/zmm863-commits/dsh-paperclip", + "page": "https://awesome-dsh-plugin.com/p/zmm863-commits/dsh-paperclip/", + "category": "ui", + "description": { + "en": "DSH Web GUI paperclip button: a 📎 button in the composer opens a file picker (drag & drop supported) and inserts file contents into the textarea.", + "zh": "在 DSH Web 输入框添加回形针按钮:点击选择文件(支持拖拽),文件内容自动插入输入框。" + }, + "npm": "dsh-paperclip", + "stars": 2, + "downloads": 1515, + "install": "dsh plugin --profile web add dsh-paperclip", + "added": "2026-08-21" + }, + { + "name": "dsh-web-preview", + "owner": "zoumutou", + "url": "https://github.com/zoumutou/dsh-web-preview", + "page": "https://awesome-dsh-plugin.com/p/zoumutou/dsh-web-preview/", + "category": "ui", + "description": { + "en": "Side web-preview panel: local static hosting, Markdown/code/image preview, one-click run of non-static projects (Cargo/npm/Go/Python) with live logs, drag files into the chat (saved to the workspace), element mark & annotate, workspace file search on 404, and link-click takeover into the side panel.", + "zh": "侧边网页预览面板:本地静态托管、Markdown/代码/图片预览、非静态项目一键运行(Cargo/npm/Go/Python)实时日志、文件直接拖入对话(保存到工作区)、网页元素标记批注、404 时工作区文件搜索、链接点击接管到侧边预览。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:zoumutou/dsh-web-preview", + "added": "2026-08-14" + }, + { + "name": "dsh-openpencil", + "owner": "ZSeven-W", + "url": "https://github.com/ZSeven-W/dsh-openpencil", + "page": "https://awesome-dsh-plugin.com/p/ZSeven-W/dsh-openpencil/", + "category": "ui", + "description": { + "en": "OpenPencil design preview and editing plugin.", + "zh": "OpenPencil 设计预览与编辑插件。" + }, + "npm": "@zseven-w/dsh-openpencil", + "stars": 152, + "downloads": 3947, + "install": "dsh plugin --profile web add @zseven-w/dsh-openpencil", + "added": "2026-08-13" + }, + { + "name": "adhdgofly-dsh-ext", + "owner": "zuoguyoupan2023", + "url": "https://github.com/zuoguyoupan2023/adhdgofly-dsh-ext", + "page": "https://awesome-dsh-plugin.com/p/zuoguyoupan2023/adhdgofly-dsh-ext/", + "category": "ui", + "description": { + "en": "Highlights parts of speech (nouns green, verbs red, adjectives/adverbs purple) in rendered DSH Web Markdown, with light/dark palettes, toggles, and stream-aware updates.", + "zh": "在 DSH Web 已渲染的 Markdown 中做词性高亮:名词绿、动词红、形容/副词紫,支持深/浅色板、词性开关与流式防抖。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:zuoguyoupan2023/adhdgofly-dsh-ext", + "added": "2026-08-16" + }, + { + "name": "dsh-selection-followup", + "owner": "zzx-dear", + "url": "https://github.com/zzx-dear/dsh-selection-followup", + "page": "https://awesome-dsh-plugin.com/p/zzx-dear/dsh-selection-followup/", + "category": "ui", + "description": { + "en": "Select any text in a DSH chat reply to get a floating Follow-up/Copy pill: one click quotes the selection into the composer so you can type your own follow-up question, or copies it.", + "zh": "选中聊天回复中的任意文字,浮出「追问/复制」气泡:一键把选中内容作为引用填入输入框(问题由你输入),或一键复制。另附安装与实现文档。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:zzx-dear/dsh-selection-followup", + "added": "2026-08-19" + }, + { + "name": "dsh-api-balance", + "owner": "02Muller25", + "url": "https://github.com/02Muller25/dsh-api-balance", + "page": "https://awesome-dsh-plugin.com/p/02Muller25/dsh-api-balance/", + "category": "usage", + "description": { + "en": "Real-time DeepSeek API account balance in the composer dock with manual or custom-interval auto-refresh.", + "zh": "输入框下方实时显示 DeepSeek API 账户余额,支持手动刷新与自定义间隔自动刷新。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:02Muller25/dsh-api-balance", + "added": "2026-08-15" + }, + { + "name": "dsh-usage-dashboard-plus", + "owner": "1HelloMan1", + "url": "https://github.com/1HelloMan1/dsh-usage-dashboard-plus", + "page": "https://awesome-dsh-plugin.com/p/1HelloMan1/dsh-usage-dashboard-plus/", + "category": "usage", + "description": { + "en": "Shows DeepSeek balance and estimated daily spend, merges external vision-call JSONL records, and adds a per-session dashboard with model statistics, TTFT, cache rate, call logs, cost estimates, filters, and CSV export.", + "zh": "显示 DeepSeek 余额与今日花费估算,合并外部视觉调用 JSONL 记录,并提供含模型统计、TTFT、缓存率、调用日志、费用估算、筛选与 CSV 导出的会话看板。" + }, + "npm": "dsh-usage-dashboard-plus", + "stars": 4, + "downloads": 586, + "install": "dsh plugin --profile web add dsh-usage-dashboard-plus", + "added": "2026-08-18" + }, + { + "name": "dsh-token-billing", + "owner": "2006spy", + "url": "https://github.com/2006spy/dsh-token-billing", + "page": "https://awesome-dsh-plugin.com/p/2006spy/dsh-token-billing/", + "category": "usage", + "description": { + "en": "Real-time token billing for DSH web: official DeepSeek CNY pricing with automatic peak/off-peak switching, hourly price sync from the official site, visual custom model prices, and multi-currency fallback.", + "zh": "DSH Web 实时 token 计费插件:官网人民币价直接计费、高峰/错峰自动切换、价格实时跟随官网、可视化自定义模型价格、多币种兜底。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:2006spy/dsh-token-billing", + "added": "2026-08-17" + }, + { + "name": "dsh-heatmap", + "owner": "283Gawin", + "url": "https://github.com/283Gawin/dsh-heatmap", + "page": "https://awesome-dsh-plugin.com/p/283Gawin/dsh-heatmap/", + "category": "usage", + "description": { + "en": "Activity heatmap in the DSH Web sidebar: GitHub-style grid of daily commits, token usage, and estimated spend, with a today stats line for all-session token totals, cache hit rate, and per-model auto-priced cost.", + "zh": "DSH Web 侧边栏活动热力图:GitHub 风格网格展示每日提交、Token 用量与估算花费,今日统计行显示全会话 Token 总量、缓存命中率与按模型自动计价的花费。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:283Gawin/dsh-heatmap", + "added": "2026-08-14" + }, + { + "name": "chicheng-stats", + "owner": "534119219", + "url": "https://github.com/534119219/chicheng-stats", + "page": "https://awesome-dsh-plugin.com/p/534119219/chicheng-stats/", + "category": "usage", + "description": { + "en": "Configurable DSH usage statistics: a sidebar text/card widget plus a detail dialog with per-provider and per-model breakdowns, cache-hit rates, token trends, and latency details (TTFT and duration).", + "zh": "高度可配置的 DSH 用量统计插件:侧边栏文字/卡片组件加统计弹窗,提供按提供方与按模型的请求/Token 统计、缓存命中率、趋势图与首字节/总耗时明细。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:534119219/chicheng-stats", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/534119219/chicheng-stats/main/assets/card-mode.png", + "https://raw.githubusercontent.com/534119219/chicheng-stats/main/assets/text-mode.png", + "https://raw.githubusercontent.com/534119219/chicheng-stats/main/assets/usage-dialog.png", + "https://raw.githubusercontent.com/534119219/chicheng-stats/main/assets/card-settings.png", + "https://raw.githubusercontent.com/534119219/chicheng-stats/main/assets/text-settings.png" + ] + }, + { + "name": "dsh-usage-billing", + "owner": "940842546", + "url": "https://github.com/940842546/dsh-usage-billing", + "page": "https://awesome-dsh-plugin.com/p/940842546/dsh-usage-billing/", + "category": "usage", + "description": { + "en": "Usage and cost statistics with peak/off-peak pricing after the 2026-08-17 rate change, a floating summary panel, per-session breakdowns, and a day/week/month/year/all usage heatmap.", + "zh": "用量与消费统计:8/17 调价前后峰谷计费,主界面汇总面板、按会话明细与日/周/月/年/累计用量热力图。" + }, + "npm": null, + "stars": 10, + "downloads": null, + "install": "dsh plugin --profile web add github:940842546/dsh-usage-billing", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/940842546/dsh-usage-billing/main/assets/screenshots/02-stats-dialog-overview.png", + "https://raw.githubusercontent.com/940842546/dsh-usage-billing/main/assets/screenshots/03-stats-dialog-charts.png", + "https://raw.githubusercontent.com/940842546/dsh-usage-billing/main/assets/screenshots/04-settings-usage-overview.png", + "https://raw.githubusercontent.com/940842546/dsh-usage-billing/main/assets/screenshots/07-stats-dialog-usd.png", + "https://raw.githubusercontent.com/940842546/dsh-usage-billing/main/assets/screenshots/en-02-stats-dialog-overview.png", + "https://raw.githubusercontent.com/940842546/dsh-usage-billing/main/assets/screenshots/en-03-stats-dialog-charts.png", + "https://raw.githubusercontent.com/940842546/dsh-usage-billing/main/assets/screenshots/en-04-settings-usage-overview.png", + "https://raw.githubusercontent.com/940842546/dsh-usage-billing/main/assets/screenshots/en-07-stats-dialog-usd.png" + ] + }, + { + "name": "dsh-session-stats-panel", + "owner": "a1113622001", + "url": "https://github.com/a1113622001/dsh-session-stats-panel", + "page": "https://awesome-dsh-plugin.com/p/a1113622001/dsh-session-stats-panel/", + "category": "usage", + "description": { + "en": "A right-side session stats panel showing average cache-hit rate, session cost, runtime, request count, cumulative tokens and account balance.", + "zh": "右侧会话统计面板,展示平均缓存命中率、会话成本、运行时长、请求数、累计 token 与账户余额。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:a1113622001/dsh-session-stats-panel", + "added": "2026-08-23" + }, + { + "name": "dsh-balance-tracker", + "owner": "aexachao", + "url": "https://github.com/aexachao/dsh-balance-tracker", + "page": "https://awesome-dsh-plugin.com/p/aexachao/dsh-balance-tracker/", + "category": "usage", + "description": { + "en": "Real DeepSeek account balance and official spend tracker in the session header, with peak/off-peak badge, monetary alert with auto-stop, and one-click top-up.", + "zh": "会话头部的真实 DeepSeek 余额与官方消费统计:高峰/空闲标签、按金额提醒与自动停止、一键充值。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:aexachao/dsh-balance-tracker", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/aexachao/dsh-balance-tracker/HEAD/assets/screenshots/capsule.png", + "https://raw.githubusercontent.com/aexachao/dsh-balance-tracker/HEAD/assets/screenshots/card.png" + ] + }, + { + "name": "model-usage-plugin", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/model-usage-plugin", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/model-usage-plugin/", + "category": "usage", + "description": { + "en": "Per-model token usage and cost estimation with DeepSeek account balance, shown in a Settings panel tab.", + "zh": "按模型统计 token 消耗并估算费用,同时显示 DeepSeek 账户余额,展示于设置面板「模型消耗」页签。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:AKS1st/model-usage-plugin", + "added": "2026-08-15" + }, + { + "name": "dsh-plugin-balance", + "owner": "Andrew111888", + "url": "https://github.com/Andrew111888/dsh-plugin-balance", + "page": "https://awesome-dsh-plugin.com/p/Andrew111888/dsh-plugin-balance/", + "category": "usage", + "description": { + "en": "A floating credit/quota widget for the DeepSeek Harness Web: shows DeepSeek / OpenCode Go balances and usage, and tracks DSH session token usage and estimated cost by day, month, and model.", + "zh": "DSH Web 的 LLM 额度/用量悬浮窗:显示 DeepSeek / OpenCode Go 余额与套餐用量,并按日、月、模型统计 DSH 会话 token 用量与费用估算。" + }, + "npm": "dsh-plugin-balance", + "stars": 4, + "downloads": 1610, + "install": "dsh plugin --profile web add dsh-plugin-balance", + "added": "2026-08-22" + }, + { + "name": "dsh-ocgo-usage", + "owner": "badai147", + "url": "https://github.com/badai147/dsh-ocgo-usage", + "page": "https://awesome-dsh-plugin.com/p/badai147/dsh-ocgo-usage/", + "category": "usage", + "description": { + "en": "Shows OpenCode Go quota usage in the web sidebar's bottom-left corner — rolling/weekly/monthly progress with a hover card, plus a Beijing-time DeepSeek peak-hour timeline.", + "zh": "在 Web 侧边栏左下角显示 OpenCode Go 用量——滚动/本周/本月配额进度与悬停明细卡片,并附按北京时间标记的 DeepSeek 高峰时段进度条。" + }, + "npm": "@badai147/dsh-ocgo-usage", + "stars": 3, + "downloads": 546, + "install": "dsh plugin --profile web add @badai147/dsh-ocgo-usage", + "added": "2026-08-19" + }, + { + "name": "dsh-opencodego-usage", + "owner": "BeiZi6", + "url": "https://github.com/BeiZi6/dsh-opencodego-usage", + "page": "https://awesome-dsh-plugin.com/p/BeiZi6/dsh-opencodego-usage/", + "category": "usage", + "description": { + "en": "OpenCodeGo quota monitor for the DSH Web GUI: a breathing indicator at the input's bottom-right (green/yellow/red by remaining share), a liquid-glass panel with rolling/weekly/monthly usage windows and reset times, auto-refreshing every 30 s; API key read from DSH credentials.", + "zh": "OpenCodeGo 剩余额度监视器:输入框右下角呼吸指示灯(按剩余额度绿/黄/红),液态玻璃面板显示滚动/周/月用量窗口与重置时间,每 30 秒自动刷新,API Key 自动读取 DSH 凭据。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:BeiZi6/dsh-opencodego-usage", + "added": "2026-08-15" + }, + { + "name": "dsh-deepseek-balance", + "owner": "Bob-Bo1", + "url": "https://github.com/Bob-Bo1/dsh-deepseek-balance", + "page": "https://awesome-dsh-plugin.com/p/Bob-Bo1/dsh-deepseek-balance/", + "category": "usage", + "description": { + "en": "Shows DeepSeek API balance and local DSH Flash and Pro usage costs.", + "zh": "在 DSH 中查看 DeepSeek API 余额及本机 Flash 和 Pro 消费统计。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Bob-Bo1/dsh-deepseek-balance", + "added": "2026-08-19" + }, + { + "name": "dsh-calculator", + "owner": "bobcat848", + "url": "https://github.com/bobcat848/dsh-calculator", + "page": "https://awesome-dsh-plugin.com/p/bobcat848/dsh-calculator/", + "category": "usage", + "description": { + "en": "DeepSeek API spend (current session and all sessions) and account balance in the aside panel, with official pricing and peak/off-peak support.", + "zh": "右侧面板展示 DeepSeek API 费用(当前会话 + 全部会话累计)与账户余额,内置官方计价与峰谷计价支持。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:bobcat848/dsh-calculator", + "added": "2026-08-14" + }, + { + "name": "dsh-balance-and-cost", + "owner": "boooooooer", + "url": "https://github.com/boooooooer/dsh-balance-and-cost", + "page": "https://awesome-dsh-plugin.com/p/boooooooer/dsh-balance-and-cost/", + "category": "usage", + "description": { + "en": "DeepSeek account balance and model cost tracking with official peak/off-peak pricing, three-tier token billing (cache miss, cache hit, output), per-model and per-session breakdowns, a composer summary bar with hover details, CSV export, and a settings panel.", + "zh": "DeepSeek 账户余额与模型消耗:官方价格表分高峰/空闲时段计价,三档计费口径(缓存未命中/命中/输出),按模型与会话统计,主页摘要条悬停明细,支持导出 CSV 与重置记录。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:boooooooer/dsh-balance-and-cost", + "added": "2026-08-21" + }, + { + "name": "dsh-context", + "owner": "bowenliang123", + "url": "https://github.com/bowenliang123/dsh-context", + "page": "https://awesome-dsh-plugin.com/p/bowenliang123/dsh-context/", + "category": "usage", + "description": { + "en": "DSH context insight panel: Context dashboard + /context command + Context browser — one-stop context lifecycle management with categorized composition, content details, evolution trends, compaction/injection events, and stats.", + "zh": "DSH 上下文洞察面板:Context 仪表盘 + /context命令 + Context 浏览器,查看 Context的分类组成、内容详情、演进趋势、压缩/注入事件、统计等一站式 Context 全生命周期管理。" + }, + "npm": "dsh-context", + "stars": 1037, + "downloads": 32099, + "install": "dsh plugin --profile web add dsh-context", + "added": "2026-08-14" + }, + { + "name": "dsh-usage-dashboard", + "owner": "Cassius0924", + "url": "https://github.com/Cassius0924/dsh-usage-dashboard", + "page": "https://awesome-dsh-plugin.com/p/Cassius0924/dsh-usage-dashboard/", + "category": "usage", + "description": { + "en": "DeepSeek usage dashboard for DSH Web: days-left estimate from recent average spend, daily/monthly/cumulative costs with period-over-period comparison, per-model cost ranking, cache savings, peak/off-peak pricing impact, and a draggable floating balance widget.", + "zh": "DSH Web 的 DeepSeek 用量仪表盘:按近期日均消耗估算可用天数,今日/本月/累计花费环比对照,分模型成本排行、缓存节省、峰谷定价影响,以及可拖动的悬浮余额窗。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Cassius0924/dsh-usage-dashboard", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/Cassius0924/dsh-usage-dashboard/HEAD/docs/hero.png", + "https://raw.githubusercontent.com/Cassius0924/dsh-usage-dashboard/HEAD/docs/widget.png", + "https://raw.githubusercontent.com/Cassius0924/dsh-usage-dashboard/HEAD/docs/balance.png", + "https://raw.githubusercontent.com/Cassius0924/dsh-usage-dashboard/HEAD/docs/overview.png", + "https://raw.githubusercontent.com/Cassius0924/dsh-usage-dashboard/HEAD/docs/peak.png", + "https://raw.githubusercontent.com/Cassius0924/dsh-usage-dashboard/HEAD/docs/cache.png", + "https://raw.githubusercontent.com/Cassius0924/dsh-usage-dashboard/HEAD/docs/models.png" + ] + }, + { + "name": "DeepSeek-Harness-Desktop#desktop-launcher-plugin", + "owner": "Ch0uHuaZ1", + "url": "https://github.com/Ch0uHuaZ1/DeepSeek-Harness-Desktop/tree/main/packages/desktop-launcher-plugin", + "page": "https://awesome-dsh-plugin.com/p/Ch0uHuaZ1/DeepSeek-Harness-Desktop--packages-desktop-launcher-plugin/", + "category": "usage", + "description": { + "en": "Adds a Desktop card to the settings page that detects the DeepSeek Harness Desktop app and launches it with one click.", + "zh": "在设置页加入「桌面端」卡片,检测 DeepSeek Harness 桌面应用并一键唤起。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:Ch0uHuaZ1/DeepSeek-Harness-Desktop#path:/packages/desktop-launcher-plugin", + "added": "2026-08-17" + }, + { + "name": "dsh-llm-cost", + "owner": "chenyinrusi", + "url": "https://github.com/chenyinrusi/dsh-llm-cost", + "page": "https://awesome-dsh-plugin.com/p/chenyinrusi/dsh-llm-cost/", + "category": "usage", + "description": { + "en": "Per-turn and session-wide LLM cost metering: a dollar cost line under each message, a session cumulative total with hover breakdown, and an LLM+web price auto-refresh tool.", + "zh": "按轮次与会话统计 LLM 成本:每条消息下方显示美元成本、会话累计总额(悬停看分项),以及 LLM+联网的价格自动刷新工具。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:chenyinrusi/dsh-llm-cost", + "added": "2026-08-17" + }, + { + "name": "dsh-deepseek-balance", + "owner": "Choi-Peng", + "url": "https://github.com/Choi-Peng/dsh-deepseek-balance", + "page": "https://awesome-dsh-plugin.com/p/Choi-Peng/dsh-deepseek-balance/", + "category": "usage", + "description": { + "en": "DeepSeek account balance in the dsh web sidebar footer with live config hot reload and an editable Settings → Plugins card.", + "zh": "在 DSH Web 侧边栏底部展示 DeepSeek 账户余额,支持配置热重载与可编辑的设置 → 插件卡片。" + }, + "npm": "@choi-p/dsh-deepseek-balance", + "stars": 2, + "downloads": 1094, + "install": "dsh plugin --profile web add @choi-p/dsh-deepseek-balance", + "added": "2026-08-17" + }, + { + "name": "dsh-codex-meter", + "owner": "ChrisZhangWG", + "url": "https://github.com/ChrisZhangWG/dsh-codex-meter", + "page": "https://awesome-dsh-plugin.com/p/ChrisZhangWG/dsh-codex-meter/", + "category": "usage", + "description": { + "en": "Adds a Settings Usage dashboard for DeepSeek API balance, official token and cost analysis, trends, context warnings, and live API activity.", + "zh": "在设置中添加 DeepSeek API 用量仪表板,显示余额、官方 Token 与费用分析、趋势、上下文警告及实时 API 活动。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:ChrisZhangWG/dsh-codex-meter", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/ChrisZhangWG/dsh-codex-meter/main/docs/settings-usage-overview.png", + "https://raw.githubusercontent.com/ChrisZhangWG/dsh-codex-meter/main/docs/settings-usage-cost-history.png" + ] + }, + { + "name": "dsh-deepseek-balance", + "owner": "CN-Leo", + "url": "https://github.com/CN-Leo/dsh-deepseek-balance", + "page": "https://awesome-dsh-plugin.com/p/CN-Leo/dsh-deepseek-balance/", + "category": "usage", + "description": { + "en": "Real-time DeepSeek account balance in the composer dock, auto-refreshing every 15 seconds with hover details.", + "zh": "在 DSH Web 输入框下方实时显示 DeepSeek 账户余额,每 15 秒自动刷新,悬停查看总额、赠送与充值明细。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:CN-Leo/dsh-deepseek-balance", + "added": "2026-08-17" + }, + { + "name": "suanzhang-dsh", + "owner": "CZ1900", + "url": "https://github.com/CZ1900/suanzhang-dsh", + "page": "https://awesome-dsh-plugin.com/p/CZ1900/suanzhang-dsh/", + "category": "usage", + "description": { + "en": "Trading-terminal style cost tracker for DeepSeek Harness: sidebar balance and today spend, per-step RMB cost table, auto-synced official pricing with peak/off-peak, cross-session and per-day summaries, tool-level attribution, and cost forecast.", + "zh": "交易终端风格的 DeepSeek Harness 计费插件:侧边栏余额与今日消费、按步骤人民币费用表、官方价自动同步(含峰谷)、跨会话与按天汇总、工具级费用归属与成本预测。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:CZ1900/suanzhang-dsh", + "added": "2026-08-25" + }, + { + "name": "dsh-plugins#dsh-usage-cost", + "owner": "DamonKoy", + "url": "https://github.com/DamonKoy/dsh-plugins/tree/main/packages/dsh-usage-cost", + "page": "https://awesome-dsh-plugin.com/p/DamonKoy/dsh-plugins--packages-dsh-usage-cost/", + "category": "usage", + "description": { + "en": "Token usage and cost tracking with daily/session budget alerts and a usage_report tool.", + "zh": "Token 用量与成本统计:日/会话预算告警 + usage_report 工具。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:DamonKoy/dsh-plugins#path:/packages/dsh-usage-cost", + "added": "2026-08-17" + }, + { + "name": "dsh-web-ui#dsh-live-stats", + "owner": "DamonKoy", + "url": "https://github.com/DamonKoy/dsh-web-ui/tree/main/packages/dsh-live-stats", + "page": "https://awesome-dsh-plugin.com/p/DamonKoy/dsh-web-ui--packages-dsh-live-stats/", + "category": "usage", + "description": { + "en": "Live token estimates and generation throughput for the dsh web GUI.", + "zh": "dsh web GUI 实时 token 估算与生成吞吐。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:DamonKoy/dsh-web-ui#path:/packages/dsh-live-stats", + "added": "2026-08-17" + }, + { + "name": "dsh-workspace", + "owner": "deepseek-dsh", + "url": "https://github.com/deepseek-dsh/dsh-workspace", + "page": "https://awesome-dsh-plugin.com/p/deepseek-dsh/dsh-workspace/", + "category": "usage", + "description": { + "en": "Sidebar usage dashboard: DeepSeek balance and today's cost, project file explorer, Git diff & history, built-in terminal, and one-click Harness updates.", + "zh": "侧边栏用量面板:DeepSeek 余额与今日费用、项目文件浏览、Git 变更与历史、内置终端,以及 Harness 一键更新。" + }, + "npm": null, + "tarball": "https://github.com/deepseek-dsh/dsh-workspace/releases/latest/download/dsh-workspace-0.1.0.tgz", + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/deepseek-dsh/dsh-workspace/releases/latest/download/dsh-workspace-0.1.0.tgz\"", + "added": "2026-08-20", + "screenshots": [ + "https://raw.githubusercontent.com/deepseek-dsh/dsh-workspace/main/assets/Screenshot-1.png", + "https://raw.githubusercontent.com/deepseek-dsh/dsh-workspace/main/assets/Screenshot-2.png" + ] + }, + { + "name": "dsh-deepseek-quota-left", + "owner": "dk33333333", + "url": "https://github.com/dk33333333/dsh-deepseek-quota-left", + "page": "https://awesome-dsh-plugin.com/p/dk33333333/dsh-deepseek-quota-left/", + "category": "usage", + "description": { + "en": "DeepSeek API quota panel collapsed into a left-edge handle for the Web UI: click to expand balance, official today's consumption (with platform token) and live conversation cost; fork of dsh-deepseek-quota.", + "zh": "DeepSeek API 额度面板折叠为左侧边框把手:点击展开查看余额、官方精确今日已消费(配置平台 token 后)与实时对话费用;dsh-deepseek-quota 的修改版。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:dk33333333/dsh-deepseek-quota-left", + "added": "2026-08-16" + }, + { + "name": "dsh-usage-lite", + "owner": "ericw0315", + "url": "https://github.com/ericw0315/dsh-usage-lite", + "page": "https://awesome-dsh-plugin.com/p/ericw0315/dsh-usage-lite/", + "category": "usage", + "description": { + "en": "Sidebar panel for account balances and local token usage, with provider/model breakdowns and a 27-week daily heatmap.", + "zh": "侧边栏面板展示账户余额与本地 Token 用量,包含供应商/模型明细和 27 周每日热力图。" + }, + "npm": "dsh-usage-lite", + "stars": 2, + "downloads": 319, + "install": "dsh plugin --profile web add dsh-usage-lite", + "added": "2026-08-25" + }, + { + "name": "dsh-usage-meter-harness", + "owner": "faith1688", + "url": "https://github.com/faith1688/dsh-usage-meter-harness", + "page": "https://awesome-dsh-plugin.com/p/faith1688/dsh-usage-meter-harness/", + "category": "usage", + "description": { + "en": "Real-time usage, cost and balance meter for DeepSeek Harness: official-API balance, cost accuracy within ¥0.02, per-turn / per-session / per-model cost ledger, live color-tiered token speed and a docked one-click popup.", + "zh": "DeepSeek Harness 实时用量、费用与余额计量:官方 API 实时查询余额、计费误差 0.02 元内,分本轮/本次会话/整个模型三级费用台账,实时分色档位 Token 速度,输入框旁弹窗即点即看。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:faith1688/dsh-usage-meter-harness", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/faith1688/dsh-usage-meter-harness/HEAD/assets/screenshot.png", + "https://raw.githubusercontent.com/faith1688/dsh-usage-meter-harness/HEAD/assets/popup.png" + ] + }, + { + "name": "deepseek-harness-control-center", + "owner": "feibi-mochi", + "url": "https://github.com/feibi-mochi/deepseek-harness-control-center", + "page": "https://awesome-dsh-plugin.com/p/feibi-mochi/deepseek-harness-control-center/", + "category": "usage", + "description": { + "en": "DeepSeek Harness control center for official balance monitoring, per-session costs and tokens, third-party token totals, completion alerts, official recharge, flexible layouts, and agent-assisted session controls.", + "zh": "DeepSeek Harness 监控、提醒、充值与会话控制中心:查看官方余额、本会话花费与 token、第三方 token 合计,提供完成提醒、官方充值、灵活布局及 Agent 辅助的会话控制。" + }, + "npm": "deepseek-harness-wallet", + "stars": 65, + "downloads": 8532, + "install": "dsh plugin --profile web add deepseek-harness-wallet", + "added": "2026-08-14" + }, + { + "name": "dsh-plugin-llm-balance", + "owner": "FengHuoLinShan", + "url": "https://github.com/FengHuoLinShan/dsh-plugin-llm-balance", + "page": "https://awesome-dsh-plugin.com/p/FengHuoLinShan/dsh-plugin-llm-balance/", + "category": "usage", + "description": { + "en": "DSH balance and quota card showing up to 3 recently used providers. Supports DeepSeek, Moonshot, Kimi For Coding, OpenAI Codex, and OpenCode Go, with auto-discovery, quota windows, reset times, color-coded status, dragging, and automatic refresh.", + "zh": "DSH 余额与配额悬浮卡片:常驻显示最近使用的最多 3 个 provider,支持 DeepSeek、Moonshot、Kimi For Coding、OpenAI Codex 和 OpenCode Go;自动发现配置,展示配额窗口与重置时间,并支持分档着色、拖动和自动刷新。" + }, + "npm": "dsh-plugin-llm-balance", + "stars": 4, + "downloads": 576, + "install": "dsh plugin --profile web add dsh-plugin-llm-balance", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/FengHuoLinShan/dsh-plugin-llm-balance/main/assets/llm-balance-preview.png" + ] + }, + { + "name": "dsh-plugin-deepseek-balance", + "owner": "fishxcode", + "url": "https://github.com/fishxcode/dsh-plugin-deepseek-balance", + "page": "https://awesome-dsh-plugin.com/p/fishxcode/dsh-plugin-deepseek-balance/", + "category": "usage", + "description": { + "en": "DeepSeek API balance, balance trend, and daily usage charts in DSH Web settings.", + "zh": "在 DSH Web 设置中展示 DeepSeek API 余额、余额趋势与每日用量图表。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:fishxcode/dsh-plugin-deepseek-balance", + "added": "2026-08-14" + }, + { + "name": "dsh-minimax-usage#trusted-plugin", + "owner": "Floating-Dreaming", + "url": "https://github.com/Floating-Dreaming/dsh-minimax-usage/tree/main/trusted-plugin", + "page": "https://awesome-dsh-plugin.com/p/Floating-Dreaming/dsh-minimax-usage--trusted-plugin/", + "category": "usage", + "description": { + "en": "Adds a \"用量\" section to the DSH Settings page that shows your MiniMax Token Plan usage (5h and weekly windows with reset countdowns), per-model breakdown, and video bonus credits; fetches `/v1/token_plan/remains` via a trusted plugin that holds the API key, auto-hides when no key is configured. MIT.", + "zh": "在 DSH 设置页「用量」section 展示 MiniMax Token Plan 的 5 小时 / 周窗口用量(带重置倒计时)、单模型明细与视频赠送额度;通过 trusted plugin 调 `/v1/token_plan/remains` 拿 key,未配置 key 时整段 section 自动隐藏。MIT。" + }, + "npm": "@floatingdeaming/minimax-usage", + "stars": 3, + "downloads": 744, + "install": "dsh plugin --profile web add @floatingdeaming/minimax-usage", + "added": "2026-08-16" + }, + { + "name": "dsh-token-cost", + "owner": "gdy01", + "url": "https://github.com/gdy01/dsh-token-cost", + "page": "https://awesome-dsh-plugin.com/p/gdy01/dsh-token-cost/", + "category": "usage", + "description": { + "en": "Per-project LLM token usage and RMB cost in the DSH Web sidebar, split by input (cache miss), cache hit, and output with model price auto-pricing.", + "zh": "在 DSH Web 侧边栏按项目展示 LLM Token 用量与人民币花费,分别统计输入(未命中)、命中与输出,并按模型标准价格自动计价。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:gdy01/dsh-token-cost", + "added": "2026-08-19" + }, + { + "name": "dsh-balance-meter", + "owner": "Ghost011118", + "url": "https://github.com/Ghost011118/dsh-balance-meter", + "page": "https://awesome-dsh-plugin.com/p/Ghost011118/dsh-balance-meter/", + "category": "usage", + "description": { + "en": "DeepSeek account balance and session cost in the composer dock, with auto-fetched official pricing and peak/off-peak support.", + "zh": "输入框 dock 显示 DeepSeek 账户余额与会话花费,自动拉取官方定价,支持高峰/低谷计价。" + }, + "npm": null, + "stars": 19, + "downloads": null, + "install": "dsh plugin --profile web add github:Ghost011118/dsh-balance-meter", + "added": "2026-08-14" + }, + { + "name": "dsh-opencode-go-quota", + "owner": "GLFzr", + "url": "https://github.com/GLFzr/dsh-opencode-go-quota", + "page": "https://awesome-dsh-plugin.com/p/GLFzr/dsh-opencode-go-quota/", + "category": "usage", + "description": { + "en": "OpenCode Go quota ring: click-to-cycle progress ring (5h/weekly/monthly) left of the model selector, colored by urgency, with reset countdowns on hover.", + "zh": "模型选择器左侧的 OpenCode Go 额度圆环:点击循环切换 5 小时/每周/每月用量窗口,按紧急程度着色(绿/蓝/橙/红),悬停显示百分比与重置倒计时。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:GLFzr/dsh-opencode-go-quota", + "added": "2026-08-14" + }, + { + "name": "context-vista", + "owner": "GooodWei", + "url": "https://github.com/GooodWei/context-vista", + "page": "https://awesome-dsh-plugin.com/p/GooodWei/context-vista/", + "category": "usage", + "description": { + "en": "A right-side floating panel and /context command for DeepSeek Harness — a live donut chart of context token usage, allocation, and estimated cost.", + "zh": "为 DeepSeek Harness 提供右侧悬浮栏以及 /context 命令,用环形图实时展示当前上下文 token 用量与分配及消费估算。" + }, + "npm": "context-vista", + "stars": 12, + "downloads": 462, + "install": "dsh plugin --profile web add context-vista", + "added": "2026-08-14" + }, + { + "name": "dsh-api-balance", + "owner": "GPIOX", + "url": "https://github.com/GPIOX/dsh-api-balance", + "page": "https://awesome-dsh-plugin.com/p/GPIOX/dsh-api-balance/", + "category": "usage", + "description": { + "en": "Floating API-balance badge for DeepSeek Harness: live balance for DeepSeek, Moonshot, OpenAI and custom endpoints; draggable, resizable, frosted-glass look, with text color adapting to the content beneath.", + "zh": "DeepSeek Harness 悬浮 API 余额徽章:实时显示 DeepSeek / Moonshot / OpenAI / 自定义接口余额;可拖动缩放、亚克力质感、文字颜色随下方内容明暗自适应。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:GPIOX/dsh-api-balance", + "added": "2026-08-15" + }, + { + "name": "dsh-cost-meter", + "owner": "Han-1413141", + "url": "https://github.com/Han-1413141/dsh-cost-meter", + "page": "https://awesome-dsh-plugin.com/p/Han-1413141/dsh-cost-meter/", + "category": "usage", + "description": { + "en": "Per-session and daily API cost, budget with usage %, official balance, history dashboard, and one-click official price sync with peak/off-peak pricing.", + "zh": "会话与当日 API 费用统计、预算图框(已用%)、官方余额、历史看板,支持峰谷计价与官方价格一键同步。" + }, + "npm": "dsh-cost-meter", + "stars": 195, + "downloads": 17152, + "install": "dsh plugin --profile web add dsh-cost-meter", + "added": "2026-08-14" + }, + { + "name": "dsh-usage-dashboard", + "owner": "Hann428", + "url": "https://github.com/Hann428/dsh-usage-dashboard", + "page": "https://awesome-dsh-plugin.com/p/Hann428/dsh-usage-dashboard/", + "category": "usage", + "description": { + "en": "DeepSeek Harness Usage tab for DeepSeek account balance, official peak/off-peak pricing, live countdown, platform usage link, and local balance warning light.", + "zh": "DeepSeek Harness 用量页签:展示 DeepSeek 账户余额、官方峰谷时段价格、动态倒计时、平台用量页入口和本地余额告警灯。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Hann428/dsh-usage-dashboard", + "added": "2026-08-20" + }, + { + "name": "dsh-deepseek-monitor", + "owner": "HaoyueQin", + "url": "https://github.com/HaoyueQin/dsh-deepseek-monitor", + "page": "https://awesome-dsh-plugin.com/p/HaoyueQin/dsh-deepseek-monitor/", + "category": "usage", + "description": { + "en": "Official DeepSeek platform monitoring inside the provider card: a live balance chip left of the composer model name plus an expandable account-details panel with per-model monthly usage and a daily stacked chart.", + "zh": "在官方供应商卡片内实时监控 DeepSeek 余额与开放平台用量——输入框工具行模型名称左侧的余额 chip,可展开的账户明细面板含按模型用量与每日堆叠图表。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:HaoyueQin/dsh-deepseek-monitor", + "added": "2026-08-28" + }, + { + "name": "dsh-usage-statistics-panel", + "owner": "HaoyueQin", + "url": "https://github.com/HaoyueQin/dsh-usage-statistics-panel", + "page": "https://awesome-dsh-plugin.com/p/HaoyueQin/dsh-usage-statistics-panel/", + "category": "usage", + "description": { + "en": "Usage statistics settings panel: 26-week activity heatmap, daily token trend with a cache hit-rate curve, per-model donut and breakdown, time-range filters, summary cards, one-time historical backfill and lossless rebuild.", + "zh": "用量统计设置面板:26 周活跃热力图、按日 Token 趋势与缓存命中率曲线、模型用量环形图与明细、时间范围筛选、汇总卡片,支持历史会话回扫与无损重建。" + }, + "npm": "dsh-usage-statistics-panel", + "stars": 3, + "downloads": 1071, + "install": "dsh plugin --profile web add dsh-usage-statistics-panel", + "added": "2026-08-23", + "screenshots": [ + "https://raw.githubusercontent.com/HaoyueQin/dsh-usage-statistics-panel/HEAD/assets/screenshots/usage-overview.png", + "https://raw.githubusercontent.com/HaoyueQin/dsh-usage-statistics-panel/HEAD/assets/screenshots/model-breakdown.png" + ] + }, + { + "name": "dsh-usage-guard", + "owner": "hb-degithub", + "url": "https://github.com/hb-degithub/dsh-usage-guard", + "page": "https://awesome-dsh-plugin.com/p/hb-degithub/dsh-usage-guard/", + "category": "usage", + "description": { + "en": "Usage statistics panel: silent per-model token collection, activity heatmap, trend charts, cost estimation and daily-limit guard.", + "zh": "用量统计面板:静默按模型采集 token、活跃热力图、趋势图、费用估算与每日超限守卫。" + }, + "npm": "dsh-usage-guard", + "stars": 1, + "downloads": 310, + "install": "dsh plugin --profile web add dsh-usage-guard", + "added": "2026-08-21" + }, + { + "name": "dsh-token-heatmap", + "owner": "Hou-DL", + "url": "https://github.com/Hou-DL/dsh-token-heatmap", + "page": "https://awesome-dsh-plugin.com/p/Hou-DL/dsh-token-heatmap/", + "category": "usage", + "description": { + "en": "Local-first token usage heatmap inside DSH settings: GitHub-style week/month/quarter/year grids, hourly usage per day, per-model and per-provider Top 5, and persisted history that survives session deletion. Zero network, zero billing.", + "zh": "DSH 设置内的本地 Token 用量热图:GitHub 风格周/月/季度/年视图、每日小时级展开、模型与供应商 Top 5,历史持久化,删除会话不丢数据。零联网、零计费。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Hou-DL/dsh-token-heatmap", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/Hou-DL/dsh-token-heatmap/HEAD/assets/screenshot-week.png", + "https://raw.githubusercontent.com/Hou-DL/dsh-token-heatmap/HEAD/assets/screenshot-month.png", + "https://raw.githubusercontent.com/Hou-DL/dsh-token-heatmap/HEAD/assets/screenshot-quarter-year.png" + ] + }, + { + "name": "dsh-balance-tide", + "owner": "huanyuLv", + "url": "https://github.com/huanyuLv/dsh-balance-tide", + "page": "https://awesome-dsh-plugin.com/p/huanyuLv/dsh-balance-tide/", + "category": "usage", + "description": { + "en": "DeepSeek account balance and session cost under the composer, with a live peak/off-peak pricing badge (Beijing time), a countdown to the next pricing switch, hover price tables, and usage advice.", + "zh": "输入框下方显示 DeepSeek 账户余额与本会话花费,余额前带峰/谷价格徽章(北京时间)与距切换倒计时,悬停看两档单价明细与使用建议。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:huanyuLv/dsh-balance-tide", + "added": "2026-08-15" + }, + { + "name": "dsh-go-balance", + "owner": "iamfromchangsha", + "url": "https://github.com/iamfromchangsha/dsh-go-balance", + "page": "https://awesome-dsh-plugin.com/p/iamfromchangsha/dsh-go-balance/", + "category": "usage", + "description": { + "en": "OpenCode Go balance pill at the right end of the composer tool row: rolling/weekly/monthly quota remaining straight from the Zen usage endpoint, with hover detail and warn/danger coloring.", + "zh": "输入框工具行右侧的 OpenCode Go 余额胶囊:滚动/每周/每月配额剩余百分比(数据直接来自 Zen 用量接口),悬停查看明细,低余额自动变黄/变红。" + }, + "npm": "dsh-go-balance", + "stars": 4, + "downloads": 587, + "install": "dsh plugin --profile web add dsh-go-balance", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/iamfromchangsha/dsh-go-balance/main/docs/screenshot-composer.png", + "https://raw.githubusercontent.com/iamfromchangsha/dsh-go-balance/main/docs/screenshot-tooltip.png", + "https://raw.githubusercontent.com/iamfromchangsha/dsh-go-balance/main/docs/screenshot-warn.png" + ] + }, + { + "name": "dsh-ibka-balance", + "owner": "ibka512", + "url": "https://github.com/ibka512/dsh-ibka-balance", + "page": "https://awesome-dsh-plugin.com/p/ibka512/dsh-ibka-balance/", + "category": "usage", + "description": { + "en": "Permanent composer-dock balance card: real-time DeepSeek API account balance with 5-minute auto-refresh, a manual refresh button, and low-balance color warnings.", + "zh": "输入框下方常驻余额卡片:实时显示 DeepSeek API 账户余额,每 5 分钟自动刷新,支持手动刷新,余额过低自动变色提醒。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:ibka512/dsh-ibka-balance", + "added": "2026-08-15" + }, + { + "name": "dsh-deepseek-usage-dashboard", + "owner": "izz-BLUE", + "url": "https://github.com/izz-BLUE/dsh-deepseek-usage-dashboard", + "page": "https://awesome-dsh-plugin.com/p/izz-BLUE/dsh-deepseek-usage-dashboard/", + "category": "usage", + "description": { + "en": "DeepSeek API usage dashboard for DSH Web: daily cache-hit/miss and output token statistics from session logs, per-model cost estimates, account balance, 7-day trends, and a composer usage summary.", + "zh": "DSH Web 的 DeepSeek API 用量仪表盘:基于会话日志统计每日缓存命中/未命中输入与输出 Token,并展示分模型费用估算、账户余额、7 日趋势和 composer 用量摘要。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:izz-BLUE/dsh-deepseek-usage-dashboard", + "added": "2026-08-15" + }, + { + "name": "dsh-model-balance", + "owner": "jacujay", + "url": "https://github.com/jacujay/dsh-model-balance", + "page": "https://awesome-dsh-plugin.com/p/jacujay/dsh-model-balance/", + "category": "usage", + "description": { + "en": "Auto-detect the current model vendor (7 built-in) and show its balance or quota in the composer input row, with custom endpoints for unsupported vendors.", + "zh": "自动识别当前模型厂商(内置 7 家),在输入框内显示余额或配额,未内置厂商可配置自定义接口。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:jacujay/dsh-model-balance", + "added": "2026-08-20" + }, + { + "name": "dsh-workspace-hub", + "owner": "Jadramcool", + "url": "https://github.com/Jadramcool/dsh-workspace-hub", + "page": "https://awesome-dsh-plugin.com/p/Jadramcool/dsh-workspace-hub/", + "category": "usage", + "description": { + "en": "DSH workspace hub: organize workspaces into color-coded folders in the sidebar (three-level drag reorder for folders/workspaces/sessions, inline edit, one-click expand/collapse, dual-channel search) with per-workspace and per-session token usage and cost stats from real session logs (official DeepSeek peak/off-peak pricing for any deepseek model, three spend calibers, hover detail cards, overview panel) plus OpenCode Go plan usage; data persists to host disk and refreshes live every 30 s.", + "zh": "DSH 工作区中枢:侧边栏将工作区按彩色分组管理(分组/工作区/会话三级拖拽排序、内联编辑、一键展开折叠、标题+消息双通道搜索),基于真实会话日志统计每工作区与每会话的 Token 与花费(任意 deepseek 模型自动按官方峰谷价计费、三种时段口径、悬停明细卡、总览面板),另含 OpenCode Go 额度;分组与结果持久化到本机磁盘,30 秒实时刷新。" + }, + "npm": "dsh-workspace-hub", + "stars": 1, + "downloads": 299, + "install": "dsh plugin --profile web add dsh-workspace-hub", + "added": "2026-08-25" + }, + { + "name": "dsh-bill", + "owner": "Jannchie", + "url": "https://github.com/Jannchie/dsh-bill", + "page": "https://awesome-dsh-plugin.com/p/Jannchie/dsh-bill/", + "category": "usage", + "description": { + "en": "Cost tracking priced from an online catalogue (models.dev + OpenRouter, 8000+ models, each call priced at its own instant): a per-turn cost line, a Cost tab attributing spend to tool output / model output / system prompt / commands, budget, monthly forecast, ~166 live-rate currencies, and a bill_stats agent tool.", + "zh": "按在线价格目录计价的费用统计(models.dev + OpenRouter,8000+ 模型,每次调用按自身时刻定价):每轮成本行、把花费归因到工具输出 / 模型输出 / 系统提示词 / 终端命令的「费用」标签页、预算、月度预测、约 166 种实时汇率货币,以及 bill_stats agent 工具。" + }, + "npm": "dsh-bill", + "stars": 4, + "downloads": 1366, + "install": "dsh plugin --profile web add dsh-bill", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/Jannchie/dsh-bill/main/docs/attribution.png", + "https://raw.githubusercontent.com/Jannchie/dsh-bill/main/docs/in-chat.png" + ] + }, + { + "name": "dsh-deepseek-quota-bar", + "owner": "jiangli07", + "url": "https://github.com/jiangli07/dsh-deepseek-quota-bar", + "page": "https://awesome-dsh-plugin.com/p/jiangli07/dsh-deepseek-quota-bar/", + "category": "usage", + "description": { + "en": "Draggable transparent balance card with a blood-bar of remaining balance vs month-opening, today/month spend (official when the platform token is configured), and session cost.", + "zh": "可拖动透明余额卡片:余额/月初额度血条、今日/本月用量(配置平台 token 后为官方精确数据)、当前对话费用。" + }, + "npm": "dsh-deepseek-quota-bar", + "stars": 4, + "downloads": 623, + "install": "dsh plugin --profile web add dsh-deepseek-quota-bar", + "added": "2026-08-15" + }, + { + "name": "dsh-deepseek-billing", + "owner": "Jolly-J", + "url": "https://github.com/Jolly-J/dsh-deepseek-billing", + "page": "https://awesome-dsh-plugin.com/p/Jolly-J/dsh-deepseek-billing/", + "category": "usage", + "description": { + "en": "DeepSeek account balance and per-session cost card in the sidebar foot.", + "zh": "侧边栏底部 DeepSeek 账户余额显示与会话费用估算卡片。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:Jolly-J/dsh-deepseek-billing", + "added": "2026-08-14" + }, + { + "name": "dsh-llm-balance", + "owner": "JonyChan8394", + "url": "https://github.com/JonyChan8394/dsh-llm-balance", + "page": "https://awesome-dsh-plugin.com/p/JonyChan8394/dsh-llm-balance/", + "category": "usage", + "description": { + "en": "Multi-provider LLM API balances under the chat input, with presets for DeepSeek, OpenRouter and SiliconFlow.", + "zh": "聊天输入框下方显示多个 LLM API 账户余额,内置 DeepSeek、OpenRouter、SiliconFlow 预设。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:JonyChan8394/dsh-llm-balance", + "added": "2026-08-17" + }, + { + "name": "dsh-token-usage", + "owner": "kelearns", + "url": "https://github.com/kelearns/dsh-token-usage", + "page": "https://awesome-dsh-plugin.com/p/kelearns/dsh-token-usage/", + "category": "usage", + "description": { + "en": "Token usage heatmap for the Web UI: daily/weekly/cumulative views over a 12-month window with light/dark themes.", + "zh": "Token 用量热力图:日/周/累计视图,12 个月窗口,支持深浅色主题。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:kelearns/dsh-token-usage", + "added": "2026-08-15" + }, + { + "name": "dsh-ui-usage-billing", + "owner": "kenz1117", + "url": "https://github.com/kenz1117/dsh-ui-usage-billing", + "page": "https://awesome-dsh-plugin.com/p/kenz1117/dsh-ui-usage-billing/", + "category": "usage", + "description": { + "en": "Sidebar billing dashboard: real usage aggregated from session logs (per-model and per-day calls/tokens/cache), cost estimated in CNY from a current multi-provider pricing catalog, subscription-plan (coding/token/agent) routes exempt, and per-provider health dots.", + "zh": "侧边栏计费仪表盘:从会话日志聚合真实用量(按模型/按日统计调用、Token、缓存),按最新多厂商官方价格估算人民币费用,订阅套餐(coding/token/agent)通道豁免计费,并带各厂商健康状态圆点。" + }, + "npm": "@kenz1117/dsh-ui-usage-billing", + "stars": 16, + "downloads": 7020, + "install": "dsh plugin --profile web add @kenz1117/dsh-ui-usage-billing", + "added": "2026-08-16" + }, + { + "name": "dsh-session-cost", + "owner": "KIDLi1412", + "url": "https://github.com/KIDLi1412/dsh-session-cost", + "page": "https://awesome-dsh-plugin.com/p/KIDLi1412/dsh-session-cost/", + "category": "usage", + "description": { + "en": "Session cost estimate in a status bar at the bottom of the conversation (per-model token pricing, CNY) plus the live DeepSeek account balance via the official balance API.", + "zh": "对话底部的会话费用状态栏:按模型逐条计价估算 Token 费用(CNY),并实时显示 DeepSeek 账户余额(官方余额接口)。" + }, + "npm": "@kidli1412/dsh-session-cost", + "stars": 3, + "downloads": 1099, + "install": "dsh plugin --profile web add @kidli1412/dsh-session-cost", + "added": "2026-08-16" + }, + { + "name": "dsh-token-heatmap", + "owner": "KIDLi1412", + "url": "https://github.com/KIDLi1412/dsh-token-heatmap", + "page": "https://awesome-dsh-plugin.com/p/KIDLi1412/dsh-token-heatmap/", + "category": "usage", + "description": { + "en": "GitHub-style daily token-usage heatmap on the new-session screen with a selectable calendar-year view, green/blue color schemes, and today / this-month / all-time totals.", + "zh": "新建会话页的 GitHub 风格每日 Token 用量热力图:可选日历年度视图、绿/蓝配色与显示开关,并附今日/本月/累计总量。" + }, + "npm": "@kidli1412/dsh-token-heatmap", + "stars": 2, + "downloads": 435, + "install": "dsh plugin --profile web add @kidli1412/dsh-token-heatmap", + "added": "2026-08-16" + }, + { + "name": "ccs-balance", + "owner": "kikomaotu", + "url": "https://github.com/kikomaotu/ccs-balance", + "page": "https://awesome-dsh-plugin.com/p/kikomaotu/ccs-balance/", + "category": "usage", + "description": { + "en": "Sync cc-switch provider balances and track token usage & cost by day/month/total with relay-aware currency settings.", + "zh": "同步 cc-switch 各 provider 余额,按日/月/总量统计 token 用量与花费,支持中转站币种与汇率设置。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:kikomaotu/ccs-balance", + "added": "2026-08-27" + }, + { + "name": "dsh-usage-vendor-stats", + "owner": "kirigayakazima", + "url": "https://github.com/kirigayakazima/dsh-usage-vendor-stats", + "page": "https://awesome-dsh-plugin.com/p/kirigayakazima/dsh-usage-vendor-stats/", + "category": "usage", + "description": { + "en": "Vendor-based usage dashboard: per-provider token/cache/output KPI, a 53-week heatmap, trend chart with hourly \"today\" view, model drilldown, cost estimation, CSV export, and health cards (TTFT, generation speed, peak context, error rate).", + "zh": "按厂商(订阅/官方API)统计用量看板:按供应商/模型拆分 Token、缓存、输出等 KPI,53 周热力图,趋势折线图(今天按小时),模型钻取,费用估算,CSV 导出,以及健康度卡片(TTFT、生成速度、峰值 Context、错误率)。" + }, + "npm": "dsh-usage-vendor-stats", + "stars": 2, + "downloads": 217, + "install": "dsh plugin --profile web add dsh-usage-vendor-stats", + "added": "2026-08-16" + }, + { + "name": "dsh-token-usage", + "owner": "LaoYueHanNi", + "url": "https://github.com/LaoYueHanNi/dsh-token-usage", + "page": "https://awesome-dsh-plugin.com/p/LaoYueHanNi/dsh-token-usage/", + "category": "usage", + "description": { + "en": "Per-request model token usage tracked to per-day JSONL files, with a stats page in Web settings: daily trend chart, per-model breakdown, and date/model filters.", + "zh": "按请求持久化模型 token 用量,Web 设置「Token 用量」统计页:按日趋势图、按模型明细表、日期/模型筛选。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:LaoYueHanNi/dsh-token-usage", + "added": "2026-08-14" + }, + { + "name": "dsh-usage-monitor", + "owner": "lcthe", + "url": "https://github.com/lcthe/dsh-usage-monitor", + "page": "https://awesome-dsh-plugin.com/p/lcthe/dsh-usage-monitor/", + "category": "usage", + "description": { + "en": "DSH Usage Monitor: auto-query provider credit balances and time-window limits from configured API keys, with input capsule and custom provider support.", + "zh": "DSH 用量监控:自动查询已配置 API Key 的供应商余额和时间窗口限制,支持输入框余量胶囊和自定义供应商。" + }, + "npm": "@lcthe/dsh-usage-monitor", + "stars": 2, + "downloads": 142, + "install": "dsh plugin --profile web add @lcthe/dsh-usage-monitor", + "added": "2026-08-23" + }, + { + "name": "dsh-token-usage", + "owner": "LeemanCheung", + "url": "https://github.com/LeemanCheung/dsh-token-usage", + "page": "https://awesome-dsh-plugin.com/p/LeemanCheung/dsh-token-usage/", + "category": "usage", + "description": { + "en": "Local-first four-bucket Token observability: persistent session/provider/model/day dashboards, trends, budget and anomaly signals, public-rate estimates, safe aggregate exports, and explicit opt-in usage or session-trajectory analysis.", + "zh": "本地优先的四 bucket Token 可观测性:持久会话/provider/model/日期仪表盘、趋势、预算与异常信号、公开费率估算、安全聚合导出,以及显式触发的用量/会话轨迹分析。" + }, + "npm": null, + "stars": 12, + "downloads": null, + "install": "dsh plugin --profile web add github:LeemanCheung/dsh-token-usage", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/LeemanCheung/dsh-token-usage/main/assets/token-usage-overview.png", + "https://raw.githubusercontent.com/LeemanCheung/dsh-token-usage/main/assets/token-usage-insights.png", + "https://raw.githubusercontent.com/LeemanCheung/dsh-token-usage/main/assets/token-usage-budget.png", + "https://raw.githubusercontent.com/LeemanCheung/dsh-token-usage/main/assets/token-usage-ai-analysis.png", + "https://raw.githubusercontent.com/LeemanCheung/dsh-token-usage/main/assets/token-usage-trajectory-analysis.png" + ] + }, + { + "name": "dsh-balance", + "owner": "LemCAE", + "url": "https://github.com/LemCAE/dsh-balance", + "page": "https://awesome-dsh-plugin.com/p/LemCAE/dsh-balance/", + "category": "usage", + "description": { + "en": "DeepSeek account balance and current-session spend estimate in a top-bar chip and settings card, with pause-aware auto-refresh, an editable official price table, a `deepseek_balance` model tool, and a bilingual UI.", + "zh": "顶栏徽章与设置卡片展示 DeepSeek 账户余额与当前会话预估花费:暂停感知的自动刷新、可编辑官方价格表、`deepseek_balance` 模型工具与中英文界面。" + }, + "npm": "@lemcae/dsh-balance", + "stars": 5, + "downloads": 1536, + "install": "dsh plugin --profile web add @lemcae/dsh-balance", + "added": "2026-08-15" + }, + { + "name": "dsh-token-monitor", + "owner": "licyer", + "url": "https://github.com/licyer/dsh-token-monitor", + "page": "https://awesome-dsh-plugin.com/p/licyer/dsh-token-monitor/", + "category": "usage", + "description": { + "en": "Header quota badge plus a usage tab tracking tokens and estimated cost per request across sessions, providers and models, with local SQLite history and cross-device import/export.", + "zh": "会话头部余量徽标 + 用量页签:按会话/供应商/模型统计 token 与预估费用,本地 SQLite 留存历史,支持跨设备导入导出。" + }, + "npm": "dsh-token-monitor", + "stars": 3, + "downloads": 431, + "install": "dsh plugin --profile web add dsh-token-monitor", + "added": "2026-08-23" + }, + { + "name": "dsh-convo-cost", + "owner": "ljr282341583", + "url": "https://github.com/ljr282341583/dsh-convo-cost", + "page": "https://awesome-dsh-plugin.com/p/ljr282341583/dsh-convo-cost/", + "category": "usage", + "description": { + "en": "Real-time cost tracking for the current DSH conversation: a composer-dock line showing the running token usage and DeepSeek official price estimate (base and peak/valley pricing).", + "zh": "当前对话实时费用统计:输入框下方一行显示运行中的 token 用量与 DeepSeek 官方价格估算(基础价与峰谷价)。" + }, + "npm": "dsh-convo-cost", + "stars": 1, + "downloads": 393, + "install": "dsh plugin --profile web add dsh-convo-cost", + "added": "2026-08-18" + }, + { + "name": "dsh-balance-widget", + "owner": "LL-cmyk-so", + "url": "https://github.com/LL-cmyk-so/dsh-balance-widget", + "page": "https://awesome-dsh-plugin.com/p/LL-cmyk-so/dsh-balance-widget/", + "category": "usage", + "description": { + "en": "A balance and cost widget for the DSH web GUI: a sidebar footer card shows the account balance, a remaining-ratio bar and today costs; clicking opens a four-tier cost breakdown (last prompt / this session / today-this-project / today-all) with term explanations and a one-click top-up link. Zero external dependencies, works on Node 24.", + "zh": "DSH Web GUI 的余额与成本小部件:侧边栏底部卡片显示账户余额、剩余比例条与今日花费;点击展开四层级成本明细(最近提问 / 本会话 / 今日·本项目 / 今日·全部),带名词解释与一键充值入口。零依赖,兼容 Node 24。" + }, + "npm": "dsh-balance-widget", + "stars": 3, + "downloads": 1822, + "install": "dsh plugin --profile web add dsh-balance-widget", + "added": "2026-08-20" + }, + { + "name": "dsh-hub-oauth-gateway", + "owner": "lninghaha", + "url": "https://github.com/lninghaha/dsh-hub-oauth-gateway", + "page": "https://awesome-dsh-plugin.com/p/lninghaha/dsh-hub-oauth-gateway/", + "category": "usage", + "description": { + "en": "DSH Web Usage Center with local SQLite history, HUD and dashboard, Hub account/quota snapshots, custom pricing and fee ledger, and CSV/JSON export, plus coding-plan OAuth and an optional loopback OpenAI/Anthropic gateway.", + "zh": "DSH Web 用量中心:本地 SQLite 历史、HUD 与仪表盘、Hub 账户与额度快照、自定义定价与订阅费用账本、CSV/JSON 导出,并含 coding 计划 OAuth 与可选回环 OpenAI/Anthropic 网关。" + }, + "npm": "dsh-hub-oauth-gateway", + "tarball": "https://github.com/lninghaha/dsh-hub-oauth-gateway/releases/download/v1.9.0/dsh-hub-oauth-gateway-1.9.0.tgz", + "stars": 1, + "downloads": 1913, + "install": "dsh plugin --profile web add dsh-hub-oauth-gateway", + "added": "2026-08-20", + "screenshots": [ + "https://raw.githubusercontent.com/lninghaha/dsh-hub-oauth-gateway/main/docs/images/usage-center-hud.png", + "https://raw.githubusercontent.com/lninghaha/dsh-hub-oauth-gateway/main/docs/images/usage-center-peek.png", + "https://raw.githubusercontent.com/lninghaha/dsh-hub-oauth-gateway/main/docs/images/usage-center-dashboard.png", + "https://raw.githubusercontent.com/lninghaha/dsh-hub-oauth-gateway/main/docs/images/usage-center-settings.png" + ] + }, + { + "name": "dsh-plugins#plugins/dsh-balance-plugin", + "owner": "luokai-demo", + "url": "https://github.com/luokai-demo/dsh-plugins/tree/main/plugins/dsh-balance-plugin", + "page": "https://awesome-dsh-plugin.com/p/luokai-demo/dsh-plugins--plugins-dsh-balance-plugin/", + "category": "usage", + "description": { + "en": "DeepSeek wallet balance at the sidebar foot: a credit-card icon with the amount tinted by remaining balance (green over ¥2, amber ¥0–2, red below), refreshed on mount, per turn-end, and on click; a signed delta floats up and fades on each balance change.", + "zh": "侧边栏底部的 DeepSeek 钱包余额:信用卡图标配余额数字,按剩余额度着色(≥ ¥2 绿色、¥0–2 黄色、≤ ¥0 红色),挂载时、每轮对话结束和点击时刷新;余额变动时带符号差额上飘淡出。" + }, + "npm": "dsh-balance-plugin", + "stars": 2, + "downloads": 738, + "install": "dsh plugin --profile web add dsh-balance-plugin", + "added": "2026-08-15" + }, + { + "name": "luxueliu-usage-command", + "owner": "luxueliu", + "url": "https://github.com/luxueliu/luxueliu-usage-command", + "page": "https://awesome-dsh-plugin.com/p/luxueliu/luxueliu-usage-command/", + "category": "usage", + "description": { + "en": "Daily cost card for your local LLM gateway: /usage reads gateway_usage.jsonl and itemizes spend per model and per hour, covering official direct, OpenRouter relay, and in-plan metered pools.", + "zh": "本地 LLM 网关当日花费价目卡:/usage 读取 gateway_usage.jsonl,按模型×分小时列出消费,覆盖官方直连、OpenRouter 中转与套餐内计费池。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:luxueliu/luxueliu-usage-command", + "added": "2026-08-23" + }, + { + "name": "dsh-session-cost", + "owner": "Lzh3070", + "url": "https://github.com/Lzh3070/dsh-session-cost", + "page": "https://awesome-dsh-plugin.com/p/Lzh3070/dsh-session-cost/", + "category": "usage", + "description": { + "en": "Composer cost chip: prices the current session DeepSeek API spend message-by-message with the official pricing table (incl. the 2026-08-17 peak/off-peak policy); hover for the breakdown, today (DSH) total and account balance. DeepSeek models only.", + "zh": "输入框右侧会话成本芯片:按官方价表(含 2026-08-17 峰谷价)逐消息计价当前会话的 DeepSeek API 花费,悬停查看计价明细、今日(DSH) 消耗与账户余额;仅 DeepSeek 模型计价,其他模型不计价不显示。" + }, + "npm": "@phynez_1103/dsh-session-cost", + "stars": 1, + "downloads": 285, + "install": "dsh plugin --profile web add @phynez_1103/dsh-session-cost", + "added": "2026-08-17" + }, + { + "name": "dsh-llm-key-rotation", + "owner": "m1khal3v", + "url": "https://github.com/m1khal3v/dsh-llm-key-rotation", + "page": "https://awesome-dsh-plugin.com/p/m1khal3v/dsh-llm-key-rotation/", + "category": "usage", + "description": { + "en": "Rotates configured LLM API keys after quota, rate-limit, or authentication failures.", + "zh": "在配额、速率限制或身份验证失败后轮换已配置的 LLM API 密钥。" + }, + "npm": "@m1khal3v/dsh-llm-key-rotation", + "stars": 2, + "downloads": 2584, + "install": "dsh plugin --profile web add @m1khal3v/dsh-llm-key-rotation", + "added": "2026-08-24" + }, + { + "name": "dsh-usage-stats", + "owner": "Make0209", + "url": "https://github.com/Make0209/dsh-usage-stats", + "page": "https://awesome-dsh-plugin.com/p/Make0209/dsh-usage-stats/", + "category": "usage", + "description": { + "en": "GitHub-style usage heatmap dashboard: per-workspace turn counts and token spend (with cache-hit rate), DeepSeek account balance, and workspace aliases.", + "zh": "GitHub 风格用量热力图看板:按工作区统计使用次数与 Token 花费(含缓存命中率)、DeepSeek 账户余额查询、工作区别名管理。" + }, + "npm": null, + "stars": 27, + "downloads": null, + "install": "dsh plugin --profile web add github:Make0209/dsh-usage-stats", + "added": "2026-08-14" + }, + { + "name": "dsh-usage-chart", + "owner": "Max-Samson", + "url": "https://github.com/Max-Samson/dsh-usage-chart", + "page": "https://awesome-dsh-plugin.com/p/Max-Samson/dsh-usage-chart/", + "category": "usage", + "description": { + "en": "Token, cost, and balance dashboard under the composer: live indicator plus zero-dependency SVG charts for per-turn usage, estimated cost, and DeepSeek account balance.", + "zh": "输入框下方的用量/成本/余额仪表盘:实时指标指示器 + 零依赖 SVG 用量图表,按轮次统计用量、估算成本并实时查询 DeepSeek 账户余额。" + }, + "npm": "dsh-usage-chart", + "stars": 11, + "downloads": 1480, + "install": "dsh plugin --profile web add dsh-usage-chart", + "added": "2026-08-14", + "screenshots": [ + "https://github.com/Max-Samson/dsh-usage-chart/blob/main/docs/images/usage-panel-demo-en-lightv1.0.0.png", + "https://github.com/Max-Samson/dsh-usage-chart/blob/main/docs/images/usage-panel-demo-zh-darkv1.0.0.png" + ] + }, + { + "name": "dsh-hotkey", + "owner": "melt502", + "url": "https://github.com/melt502/dsh-hotkey", + "page": "https://awesome-dsh-plugin.com/p/melt502/dsh-hotkey/", + "category": "usage", + "description": { + "en": "Configurable VS Code style keyboard shortcuts for the DSH web GUI, with a settings-page keybinding editor, conflict detection and JSON import/export.", + "zh": "为 DSH Web GUI 提供可配置的 VS Code 风格键盘快捷键,内置设置页改键、冲突检测与 JSON 导入导出。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:melt502/dsh-hotkey", + "added": "2026-08-27" + }, + { + "name": "dsh-cost-dashboard", + "owner": "mike-lee0120", + "url": "https://github.com/mike-lee0120/dsh-cost-dashboard", + "page": "https://awesome-dsh-plugin.com/p/mike-lee0120/dsh-cost-dashboard/", + "category": "usage", + "description": { + "en": "Cost dashboard for DeepSeek Harness: aggregates model input/output/cache tokens across all local sessions, prices them with a builtin table (including DeepSeek peak/off-peak), and shows daily trends plus per-model and per-session tables.", + "zh": "费用看板:汇总本机全部会话的模型输入/输出/缓存 token,按内置价格表计费(含 DeepSeek 峰谷),展示每日趋势与按模型/会话明细。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:mike-lee0120/dsh-cost-dashboard", + "added": "2026-08-19" + }, + { + "name": "dsh-quota", + "owner": "Minokun", + "url": "https://github.com/Minokun/dsh-quota", + "page": "https://awesome-dsh-plugin.com/p/Minokun/dsh-quota/", + "category": "usage", + "description": { + "en": "Bottom-right quota pill and panel aggregating membership quotas across AI platforms (Kimi Code, DeepSeek, Zhipu Coding Plan, BigModel, Qwen, Scnet, TokenRouter, SupaWriter), auto-syncing API keys from the DSH credentials domain with colored usage bars.", + "zh": "右下角「会员额度」悬浮球与面板,聚合各 AI 平台套餐额度(Kimi Code、DeepSeek、智谱 Coding Plan、BigModel、通义千问、超算、TokenRouter、SupaWriter),API key 自动同步自 DSH 凭证域,彩色进度条展示用量。" + }, + "npm": "dsh-quota", + "stars": 2, + "downloads": 2093, + "install": "dsh plugin --profile web add dsh-quota", + "added": "2026-08-19" + }, + { + "name": "dsh-token-stats", + "owner": "MoonlitDropOfBlood", + "url": "https://github.com/MoonlitDropOfBlood/dsh-token-stats", + "page": "https://awesome-dsh-plugin.com/p/MoonlitDropOfBlood/dsh-token-stats/", + "category": "usage", + "description": { + "en": "Token usage statistics for DeepSeek Harness: per-model daily and range charts plus a yearly contributions heatmap.", + "zh": "DeepSeek Harness 的 Token 用量统计:近 7 天/30 天按模型的柱状图、饼图,外加近一年每日活跃热力图。" + }, + "npm": "@duke-dsh-plugins/dsh-token-stats", + "stars": 2, + "downloads": 692, + "install": "dsh plugin --profile web add @duke-dsh-plugins/dsh-token-stats", + "added": "2026-08-23" + }, + { + "name": "dsh-token-anxiety", + "owner": "mov-eax-eax", + "url": "https://github.com/mov-eax-eax/dsh-token-anxiety", + "page": "https://awesome-dsh-plugin.com/p/mov-eax-eax/dsh-token-anxiety/", + "category": "usage", + "description": { + "en": "Live per-task token cost widget: peak/valley pricing windows, projected post-hike costs, sortable per-task cost table, multi-currency (COP/USD/CNY + ~40 more) with FX rates, and one-click streamed LLM explain of expensive tasks.", + "zh": "实时按任务追踪 token 成本的小部件:高峰/低谷定价时段、涨价后预测成本、可排序的每任务成本表、多币种(默认 COP/USD/CNY,另有 40 余种)与汇率,以及一键流式解释高成本任务。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:mov-eax-eax/dsh-token-anxiety", + "added": "2026-08-16" + }, + { + "name": "dsh-liangwengu", + "owner": "mozhuanzuojing", + "url": "https://github.com/mozhuanzuojing/dsh-liangwengu", + "page": "https://awesome-dsh-plugin.com/p/mozhuanzuojing/dsh-liangwengu/", + "category": "usage", + "description": { + "en": "DeepSeek API pricing reminder with a game HUD: Beijing-time countdowns to peak/off-peak windows, a draggable HP capsule with day/moonlight themes, and a nine-realm cultivation system with breakthrough animations.", + "zh": "按北京时间提醒 DeepSeek API 定价高峰/空闲时段的游戏化插件:可拖动血条胶囊、日夜主题、隐藏彩蛋与修仙突破动画。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:mozhuanzuojing/dsh-liangwengu", + "added": "2026-08-26" + }, + { + "name": "mmx-quota-tool", + "owner": "mtty-ai", + "url": "https://github.com/mtty-ai/mmx-quota-tool", + "page": "https://awesome-dsh-plugin.com/p/mtty-ai/mmx-quota-tool/", + "category": "usage", + "description": { + "en": "MiniMax token-plan quota dock for the Web UI: a drop-shape 5h-usage % chip in the conversation input area, click for a detail panel listing every model's 5h & weekly usage and reset countdown, auto-hidden when the active default model is not a MiniMax model.", + "zh": "DSH 网页 MiniMax 配额悬浮卡:会话输入区显示水滴形 5h 已用百分比,点击展开详情面板,列出每个模型的 5h/周用量与重置倒计时,当前默认模型非 MiniMax 时自动隐藏。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:mtty-ai/mmx-quota-tool", + "added": "2026-08-16" + }, + { + "name": "token-usage-counter", + "owner": "Mu-scorpio", + "url": "https://github.com/Mu-scorpio/token-usage-counter", + "page": "https://awesome-dsh-plugin.com/p/Mu-scorpio/token-usage-counter/", + "category": "usage", + "description": { + "en": "Persistent provider-reported token usage totals with per-session, per-model, and daily activity statistics.", + "zh": "持久化 provider 上报的 Token 用量统计,支持按会话、按模型和按日活跃度查看。" + }, + "npm": "dsh-token-usage-counter", + "stars": 2, + "downloads": 797, + "install": "dsh plugin --profile web add dsh-token-usage-counter", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/Mu-scorpio/token-usage-counter/HEAD/assets/usage-stats-overview.png", + "https://raw.githubusercontent.com/Mu-scorpio/token-usage-counter/HEAD/assets/usage-stats-hover.png" + ] + }, + { + "name": "dsh-gateway-wallet", + "owner": "MuAllen", + "url": "https://github.com/MuAllen/dsh-gateway-wallet", + "page": "https://awesome-dsh-plugin.com/p/MuAllen/dsh-gateway-wallet/", + "category": "usage", + "description": { + "en": "Live remaining balance for the current API key from Sub2API, New API, or official DeepSeek, plus actual spend when the site publishes it, not a local token estimate.", + "zh": "读取当前 API key 在 Sub2API、New API 或 DeepSeek 官方账本上的剩余额度;站点公布实扣时才显示实扣,不是本地 token 估算。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:MuAllen/dsh-gateway-wallet", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/MuAllen/dsh-gateway-wallet/HEAD/assets/sidebar.png", + "https://raw.githubusercontent.com/MuAllen/dsh-gateway-wallet/HEAD/assets/panel-top.png", + "https://raw.githubusercontent.com/MuAllen/dsh-gateway-wallet/HEAD/assets/panel-bottom.png" + ] + }, + { + "name": "dsh-usage-stats-plus", + "owner": "Nixz0824", + "url": "https://github.com/Nixz0824/dsh-usage-stats-plus", + "page": "https://awesome-dsh-plugin.com/p/Nixz0824/dsh-usage-stats-plus/", + "category": "usage", + "description": { + "en": "DSH usage and balance panel with official live balance, per-event official peak/off-peak pricing, all-channel spend estimates from daily balance snapshots, and a Codex-style 12-month token activity timeline. Fork of Ychris12138/dsh-usage-stats.", + "zh": "DSH 用量与余额面板:官方余额实时拉取、逐事件官方峰谷计价、基于余额快照的全渠道消耗估算、Codex 风格 12 个月 Token 活动时间轴。Fork 自 Ychris12138/dsh-usage-stats。" + }, + "npm": null, + "tarball": "https://github.com/Nixz0824/dsh-usage-stats-plus/releases/latest/download/dsh-usage-stats-plus-0.3.0.tgz", + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/Nixz0824/dsh-usage-stats-plus/releases/latest/download/dsh-usage-stats-plus-0.3.0.tgz\"", + "added": "2026-08-18" + }, + { + "name": "dsh-usage-monitor", + "owner": "NOirBRight", + "url": "https://github.com/NOirBRight/dsh-usage-monitor", + "page": "https://awesome-dsh-plugin.com/p/NOirBRight/dsh-usage-monitor/", + "category": "usage", + "description": { + "en": "Settings usage dashboard: tokens, requests, output tokens, and cache-hit rate from session logs, with a stacked chart and a breakdown table by provider, model, or workspace.", + "zh": "设置页用量仪表盘:从会话日志汇总 token、请求、输出 token 与缓存命中率,含堆叠图,并按供应商、模型或工作区分组列表。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:NOirBRight/dsh-usage-monitor", + "added": "2026-08-18" + }, + { + "name": "dsh-spend", + "owner": "nonewind", + "url": "https://github.com/nonewind/dsh-spend", + "page": "https://awesome-dsh-plugin.com/p/nonewind/dsh-spend/", + "category": "usage", + "description": { + "en": "Token usage and estimated spend for the dsh web UI: floating panel with per-model, per-day, and per-session stats.", + "zh": "DSH Web 用量与费用统计插件:右下角悬浮窗,按模型/按天/按会话多维聚合与预计花费。" + }, + "npm": "dsh-spend", + "stars": 9, + "downloads": 2837, + "install": "dsh plugin --profile web add dsh-spend", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/nonewind/dsh-spend/main/docs/screenshots/dashboard.png", + "https://raw.githubusercontent.com/nonewind/dsh-spend/main/docs/screenshots/details-window.png" + ] + }, + { + "name": "dsh-better-stats", + "owner": "null5069", + "url": "https://github.com/null5069/dsh-better-stats", + "page": "https://awesome-dsh-plugin.com/p/null5069/dsh-better-stats/", + "category": "usage", + "description": { + "en": "Composer stats strip for the DSH Web UI: official CNY pricing with auto-synced peak/off-peak tiers, per-model accounting, live timers, subagent-tree merging, DeepSeek account balance, budget alerts, and streaming cost estimation.", + "zh": "DSH Web 输入框下方的增强统计条:官方人民币计价(峰谷分时、价目自动同步)、多模型分账、实时计时、子代理树合并、余额直连、预算预警与流式成本估算。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:null5069/dsh-better-stats", + "added": "2026-08-27" + }, + { + "name": "dsh-ocgo-lite", + "owner": "OK-wx", + "url": "https://github.com/OK-wx/dsh-ocgo-lite", + "page": "https://awesome-dsh-plugin.com/p/OK-wx/dsh-ocgo-lite/", + "category": "usage", + "description": { + "en": "OpenCode Go usage bar in the composer dock: rolling/weekly/monthly quota rings, DSH-session token & cost stats priced with live official rates, per-model and current-session scope breakdowns with live per-session updates, one-click API key copy.", + "zh": "输入框下方常驻 OpenCode Go 用量条:5 小时/每周/每月配额圆环,DSH 会话 token 与花费统计(官方实时定价),支持按模型与按「本次会话」范围联动明细(本次会话实时更新),一键复制 API Key。" + }, + "npm": null, + "stars": 11, + "downloads": null, + "install": "dsh plugin --profile web add github:OK-wx/dsh-ocgo-lite", + "added": "2026-08-16" + }, + { + "name": "dsh-all-usage", + "owner": "ParticleLight", + "url": "https://github.com/ParticleLight/dsh-all-usage", + "page": "https://awesome-dsh-plugin.com/p/ParticleLight/dsh-all-usage/", + "category": "usage", + "description": { + "en": "Analyze usage across models, providers, workspaces, and time ranges — including custom start/end dates over the full retained history — with live cache-hit visibility and separate model and provider breakdowns. Includes a 53-week GitHub-style heatmap, usage streaks, cache-efficiency trends, DeepSeek account balance, workspace aliases, CSV export, and a durable ledger that preserves successfully flushed usage after session deletion and rebuilds incrementally — on restart, unchanged sessions are applied from the ledger without re-reading their logs (a per-session log revision is the change signal), and only changed/new events are folded. Token accounting is explicit: input is fresh (cache read/write and reasoning are separate buckets) and zero-usage replays never overwrite recorded usage. Chinese uses local time, English uses UTC; API access is loopback-only.", + "zh": "支持跨多个模型、供应商、工作区与时间范围进行统一分析,并可在全部保留历史中自定义起止日期;实时呈现缓存命中率,并可分别查看模型和供应商的分类数据;提供 53 周 GitHub 风格热力图、连续使用和缓存效率趋势,支持查询 DeepSeek 账户余额、管理工作区别名及导出 CSV 数据;独立账本在会话删除前持久化用量,增量重建只处理新增事件;重启时未变化会话不回读日志、直接复用账本(以每会话日志 revision 为变更信号),全 0 用量的重放不会覆盖已记录的真实用量,Token 口径显式声明(输入不含缓存命中,缓存读写与推理独立成桶)。中文使用本地时区,英文统一使用 UTC;API 仅限本机访问。" + }, + "npm": "dsh-all-usage", + "tarball": "https://github.com/ParticleLight/dsh-all-usage/releases/download/v1.0.8/dsh-all-usage-1.0.8.tgz", + "stars": 4, + "downloads": 862, + "install": "dsh plugin --profile web add dsh-all-usage", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/ParticleLight/dsh-all-usage/HEAD/assets/screenshot-1.png", + "https://raw.githubusercontent.com/ParticleLight/dsh-all-usage/HEAD/assets/screenshot-2.png" + ] + }, + { + "name": "dsh-meow-cachebilling", + "owner": "Phant0Meow", + "url": "https://github.com/Phant0Meow/dsh-meow-cachebilling", + "page": "https://awesome-dsh-plugin.com/p/Phant0Meow/dsh-meow-cachebilling/", + "category": "usage", + "description": { + "en": "Per-round cache billing inside the composer context-meter popover: what the current call spends on cache hits, misses, and output in CNY, with automatic official peak/off-peak and per-model pricing; hidden on non-official DeepSeek routes.", + "zh": "点开输入框旁的上下文圆环即见本轮账单:缓存命中/未命中/输出各花多少钱(¥),官方峰谷价与模型分价自动判定;非 DeepSeek 官方路由不显示。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Phant0Meow/dsh-meow-cachebilling", + "added": "2026-08-24" + }, + { + "name": "dsh-personal-center", + "owner": "PolinniZhong", + "url": "https://github.com/PolinniZhong/dsh-personal-center", + "page": "https://awesome-dsh-plugin.com/p/PolinniZhong/dsh-personal-center/", + "category": "usage", + "description": { + "en": "Personal center for DeepSeek Harness: cross-session usage statistics, per-model cost estimation, global custom instructions, a data-driven desktop pet, and a conversation status overview, all local and offline.", + "zh": "DeepSeek Harness 个人中心:跨会话用量统计、按模型成本估算、全局自定义指令、数据驱动的桌面宠物与会话状态概览,纯本地离线运行。" + }, + "npm": "dsh-personal-center", + "stars": 23, + "downloads": 272, + "install": "dsh plugin --profile web add dsh-personal-center", + "added": "2026-08-25", + "screenshots": [ + "https://raw.githubusercontent.com/PolinniZhong/dsh-personal-center/main/docs/screenshots/light-profile.png", + "https://raw.githubusercontent.com/PolinniZhong/dsh-personal-center/main/docs/screenshots/dark-pet.png", + "https://raw.githubusercontent.com/PolinniZhong/dsh-personal-center/main/docs/screenshots/pet-emotions.gif", + "https://raw.githubusercontent.com/PolinniZhong/dsh-personal-center/main/docs/screenshots/dark-model-cost.png" + ] + }, + { + "name": "dsh-token-viewer", + "owner": "qwert702", + "url": "https://github.com/qwert702/dsh-token-viewer", + "page": "https://awesome-dsh-plugin.com/p/qwert702/dsh-token-viewer/", + "category": "usage", + "description": { + "en": "CC Switch-style token consumption statistics for the dsh web GUI: per-request usage log, real-consumption hero, request-time-bucketed trend chart, per-model peak/off-peak list pricing, per-project and per-model stats, and account balance.", + "zh": "dsh 网页端 CC Switch 风格 Token 消耗统计:按请求的用量日志、真实消耗 Hero、按请求提交时间分桶的趋势图、按模型峰谷牌价计费、项目/模型统计与账户余额。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:qwert702/dsh-token-viewer", + "added": "2026-08-20" + }, + { + "name": "balance-dsh", + "owner": "Rannist", + "url": "https://github.com/Rannist/balance-dsh", + "page": "https://awesome-dsh-plugin.com/p/Rannist/balance-dsh/", + "category": "usage", + "description": { + "en": "Shows DeepSeek account balance and current-session token and cost usage in the sidebar footer and composer dock, including peak and off-peak billing and subagent consumption.", + "zh": "在侧边栏底部和输入框上方显示你的 DeepSeek 账户余额与当前会话 token 及费用消耗,支持高峰/空闲峰谷计费与子代理消耗统计。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Rannist/balance-dsh", + "added": "2026-08-28" + }, + { + "name": "dsh-balance", + "owner": "rayzhu1109", + "url": "https://github.com/rayzhu1109/dsh-balance", + "page": "https://awesome-dsh-plugin.com/p/rayzhu1109/dsh-balance/", + "category": "usage", + "description": { + "en": "Real-time DeepSeek account balance in the sidebar footer with a 5-day spending chart.", + "zh": "在侧边栏底部实时显示 DeepSeek 账户余额,点击展开最近 5 天消费柱状图。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:rayzhu1109/dsh-balance", + "added": "2026-08-23" + }, + { + "name": "dsh-whale-report", + "owner": "SenmuuuuW", + "url": "https://github.com/SenmuuuuW/dsh-whale-report", + "page": "https://awesome-dsh-plugin.com/p/SenmuuuuW/dsh-whale-report/", + "category": "usage", + "description": { + "en": "DeepTrace — deterministic agent reports from session logs (daily/weekly/monthly/yearly/custom): cost & token breakdown, 8 insight rules, collaboration review, live provider balance, PDF/PNG/HTML export; read-only, never rewrites history.", + "zh": "深迹 DeepTrace — 从会话日志生成日报/周报/月报/年报/自定义区间报告:成本与 Token 拆解、8 条确定性洞察、协作复盘、实时模型余额、PDF/PNG/HTML 导出;只读,绝不改写历史。" + }, + "npm": "dsh-whale-report", + "stars": 29, + "downloads": 328, + "install": "dsh plugin --profile web add dsh-whale-report", + "added": "2026-08-16" + }, + { + "name": "ds-api-usage", + "owner": "Sev7een", + "url": "https://github.com/Sev7een/ds-api-usage", + "page": "https://awesome-dsh-plugin.com/p/Sev7een/ds-api-usage/", + "category": "usage", + "description": { + "en": "DeepSeek API balance and 24-hour usage dashboard in Settings, with estimated spend, token counts, request counts, and an hourly timeline.", + "zh": "在设置页展示 DeepSeek API 余额与最近 24 小时用量,包括估算消费、Token、请求次数和按小时时间线。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:Sev7een/ds-api-usage", + "added": "2026-08-14" + }, + { + "name": "dsh-whale-meter", + "owner": "Shiye-10Pages", + "url": "https://github.com/Shiye-10Pages/dsh-whale-meter", + "page": "https://awesome-dsh-plugin.com/p/Shiye-10Pages/dsh-whale-meter/", + "category": "usage", + "description": { + "en": "Usage tiers and shareable stats cards: 🐟→🐳 ranks by monthly token burn with a locally-computed estimated percentile, precise pricing for 46 models across 6 vendors (including size-tiered Chinese pricing), backfill of pre-install sessions, and old-vs-new comparison across the 2026-08-17 rate change. Local-only, no telemetry.", + "zh": "用量段位与战绩卡片:按本月 token 消耗评 🐟→🐳 五档段位,分位本地估算(零上报);6 家厂商 46 个模型精准计价,支持国内厂商按输入长度分档;自动回填安装前的历史会话;8·17 调价前后同一份用量的新旧价对比。数据全在本机,无遥测。" + }, + "npm": "dsh-whale-meter", + "stars": 10, + "downloads": 460, + "install": "dsh plugin --profile web add dsh-whale-meter", + "added": "2026-08-17" + }, + { + "name": "dsh-liquid-glass-balance-card", + "owner": "SoDaZilla-zzz", + "url": "https://github.com/SoDaZilla-zzz/dsh-liquid-glass-balance-card", + "page": "https://awesome-dsh-plugin.com/p/SoDaZilla-zzz/dsh-liquid-glass-balance-card/", + "category": "usage", + "description": { + "en": "Draggable 3D liquid-glass floating card for the DSH web GUI: DeepSeek API balance, cumulative spend/token stats (today/yesterday/7d/30d/all), adjustable glass/3D/color controls, and one-click top-up.", + "zh": "DSH 网页右上角可拖动 3D 液态玻璃余额卡片:显示 DeepSeek API 余额、累计消费与 Token 统计(今天/昨天/近7天/近30天/全部),玻璃参数/3D 厚度/颜色均可调,内置官方充值入口。" + }, + "npm": "dsh-liquid-glass-balance-card", + "stars": 6, + "downloads": 472, + "install": "dsh plugin --profile web add dsh-liquid-glass-balance-card", + "added": "2026-08-16" + }, + { + "name": "dsh-status-bar", + "owner": "Starlight-bananice", + "url": "https://github.com/Starlight-bananice/dsh-status-bar", + "page": "https://awesome-dsh-plugin.com/p/Starlight-bananice/dsh-status-bar/", + "category": "usage", + "description": { + "en": "A configurable 17-segment session status bar for the dsh composer dock with live TPS, per-model cost estimation, and a usage & cost dialog.", + "zh": "输入栏下可配置的 17 段会话状态栏,提供实时 TPS、按模型分别计价的费用估算,以及用量与费用弹窗。" + }, + "npm": "@bananiceee/dsh-status-bar", + "stars": 7, + "downloads": 615, + "install": "dsh plugin --profile web add @bananiceee/dsh-status-bar", + "added": "2026-08-17" + }, + { + "name": "dsh-balance-plugin", + "owner": "stevenx65", + "url": "https://github.com/stevenx65/dsh-balance-plugin", + "page": "https://awesome-dsh-plugin.com/p/stevenx65/dsh-balance-plugin/", + "category": "usage", + "description": { + "en": "DeepSeek balance and token usage in the web sidebar, with a today/all-time toggle and provider filtering.", + "zh": "dsh 网页侧边栏的 DeepSeek 余额与 token 用量监控:今日/累计切换,并按 provider 过滤其他厂商。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:stevenx65/dsh-balance-plugin", + "added": "2026-08-14" + }, + { + "name": "dsh-plugin-ds-balance", + "owner": "storyOuO", + "url": "https://github.com/storyOuO/dsh-plugin-ds-balance", + "page": "https://awesome-dsh-plugin.com/p/storyOuO/dsh-plugin-ds-balance/", + "category": "usage", + "description": { + "en": "DeepSeek API balance + per-call cost tracker: durable per-call cost ledger from real usage chunks with official Beijing peak/off-peak (x2) pricing, 60 s balance polling, and a sidebar footer action showing balance and today's spend.", + "zh": "DeepSeek 余额与逐次调用成本追踪:基于真实 usage 的持久化成本账本,支持官方高峰/低谷定价(北京时间 ×2),60 秒余额轮询,侧栏底部显示余额与今日消耗。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:storyOuO/dsh-plugin-ds-balance", + "added": "2026-08-27" + }, + { + "name": "dsh-cost-meter", + "owner": "Sttrevens", + "url": "https://github.com/Sttrevens/dsh-cost-meter", + "page": "https://awesome-dsh-plugin.com/p/Sttrevens/dsh-cost-meter/", + "category": "usage", + "description": { + "en": "Per-turn USD cost badge in the Web UI: session total in the header and per-turn cost in each message footer, with a hover breakdown.", + "zh": "Web UI 美元成本徽标:头部显示会话总成本、每条回复结尾显示该轮成本,悬停看分项。" + }, + "npm": "@steven-wu/dsh-cost-meter", + "stars": 3, + "downloads": 601, + "install": "dsh plugin --profile web add @steven-wu/dsh-cost-meter", + "added": "2026-08-14" + }, + { + "name": "dsh-musage", + "owner": "Thedeergod666", + "url": "https://github.com/Thedeergod666/dsh-musage", + "page": "https://awesome-dsh-plugin.com/p/Thedeergod666/dsh-musage/", + "category": "usage", + "description": { + "en": "Shows coding-plan usage and balance for 5 AI providers (MiniMax, DeepSeek, Kimi, OpenRouter, Zhipu) beside the composer input, switching with the selected model and reusing API keys configured in DSH model settings.", + "zh": "在 composer 输入框旁显示 5 家 AI 套餐用量与余额(MiniMax、DeepSeek、Kimi、OpenRouter、智谱),跟随当前模型自动切换,复用 DSH 模型设置里已配的 API Key。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Thedeergod666/dsh-musage", + "added": "2026-08-24" + }, + { + "name": "dsh-usage-meter", + "owner": "V-dev-388", + "url": "https://github.com/V-dev-388/dsh-usage-meter", + "page": "https://awesome-dsh-plugin.com/p/V-dev-388/dsh-usage-meter/", + "category": "usage", + "description": { + "en": "Settings-page usage dashboard: per-provider/model token summary across all sessions, with today/7-day/30-day CSS-bar trends and a cache-hit rate.", + "zh": "设置页用量仪表盘:按服务商/模型汇总全部会话 token 用量,含今日/近 7 天/近 30 天趋势柱状图与缓存命中率。" + }, + "npm": "dsh-usage-meter", + "stars": 1, + "downloads": 509, + "install": "dsh plugin --profile web add dsh-usage-meter", + "added": "2026-08-15" + }, + { + "name": "dsh-opencode-go-usage", + "owner": "v587d", + "url": "https://github.com/v587d/dsh-opencode-go-usage", + "page": "https://awesome-dsh-plugin.com/p/v587d/dsh-opencode-go-usage/", + "category": "usage", + "description": { + "en": "OpenCode Go subscription usage (rolling/weekly/monthly windows with reset countdowns) in the composer dock, with a built-in credential editor.", + "zh": "在输入框上方 dock 显示 OpenCode Go 订阅用量(5h 滚动/每周/每月窗口与重置倒计时),内置凭据编辑器。" + }, + "npm": null, + "stars": 10, + "downloads": null, + "install": "dsh plugin --profile web add github:v587d/dsh-opencode-go-usage", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/v587d/dsh-opencode-go-usage/main/assets/custom-footer.png", + "https://raw.githubusercontent.com/v587d/dsh-opencode-go-usage/main/assets/usage-detail.png", + "https://raw.githubusercontent.com/v587d/dsh-opencode-go-usage/main/assets/set-cookie-wid.png" + ] + }, + { + "name": "dsh-agent-budget", + "owner": "vibeinging", + "url": "https://github.com/vibeinging/dsh-agent-budget", + "page": "https://awesome-dsh-plugin.com/p/vibeinging/dsh-agent-budget/", + "category": "usage", + "description": { + "en": "Agent-tree token budget management.", + "zh": "agent 树 token 预算管理。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:vibeinging/dsh-agent-budget", + "added": "2026-08-13" + }, + { + "name": "dsh-opencode-usage", + "owner": "vinyumao", + "url": "https://github.com/vinyumao/dsh-opencode-usage", + "page": "https://awesome-dsh-plugin.com/p/vinyumao/dsh-opencode-usage/", + "category": "usage", + "description": { + "en": "OpenCode Go plan usage display for the DSH web GUI: a persistent badge under the composer shows rolling/weekly/monthly usage percents with reset countdowns; click to expand a card; agents can query the balance via the `opencode_go_usage` tool.", + "zh": "OpenCode Go 套餐用量显示:输入框下方常驻徽章显示滚动 / 每周 / 每月用量百分比与重置倒计时,点击展开进度卡片;Agent 可经 `opencode_go_usage` 工具随时查询余额。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:vinyumao/dsh-opencode-usage", + "added": "2026-08-16" + }, + { + "name": "dsh-quota-panel", + "owner": "wenzetan", + "url": "https://github.com/wenzetan/dsh-quota-panel", + "page": "https://awesome-dsh-plugin.com/p/wenzetan/dsh-quota-panel/", + "category": "usage", + "description": { + "en": "Bottom-right quota capsule that auto-discovers every configured provider (DeepSeek, OpenRouter, SiliconFlow, GLM, one-api/new-api, coding plans) and shows balance or rolling 5-hour and weekly usage, with per-provider visibility, alert thresholds and proxy support.", + "zh": "右下角额度胶囊:自动发现所有已配置 Key 的供应商(DeepSeek、OpenRouter、SiliconFlow、GLM、one-api/new-api、各类 Coding 套餐),显示余额或 5 小时/周滚动用量,支持按供应商设置可见性、告警阈值与代理。" + }, + "npm": "dsh-quota-panel", + "stars": 6, + "downloads": 2228, + "install": "dsh plugin --profile web add dsh-quota-panel", + "added": "2026-08-15" + }, + { + "name": "dsh-plugin-hub#packages/dsh-opencode-usage", + "owner": "wingsky-1", + "url": "https://github.com/wingsky-1/dsh-plugin-hub/tree/main/packages/dsh-opencode-usage", + "page": "https://awesome-dsh-plugin.com/p/wingsky-1/dsh-plugin-hub--packages-dsh-opencode-usage/", + "category": "usage", + "description": { + "en": "OpenCode Go plan usage floating pill: three-window consumption percentages (rolling/weekly/monthly) with mini area charts, backed by background sampling against the official /v1/usage API.", + "zh": "OpenCode Go 套餐用量悬浮框:三窗口(rolling/weekly/monthly)消耗百分比常驻展示,展开为迷你面积图;后台采样官方 /v1/usage 接口,零本地计算。" + }, + "npm": "@wingsky-1/dsh-opencode-usage", + "stars": 5, + "downloads": 1404, + "install": "dsh plugin --profile web add @wingsky-1/dsh-opencode-usage", + "added": "2026-08-18" + }, + { + "name": "dsh-reader", + "owner": "Wodexinhaoleng-Kasssa", + "url": "https://github.com/Wodexinhaoleng-Kasssa/dsh-reader", + "page": "https://awesome-dsh-plugin.com/p/Wodexinhaoleng-Kasssa/dsh-reader/", + "category": "usage", + "description": { + "en": "In-browser novel reader for the dsh web GUI: online book-source search, chapter-by-chapter reading in a chat-style view, and whole-book TXT download.", + "zh": "DSH Web GUI 在线小说阅读器:书源搜索、聊天式阅读界面、整本下载 TXT 到本地。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Wodexinhaoleng-Kasssa/dsh-reader", + "added": "2026-08-28" + }, + { + "name": "dsh-damage-pulse", + "owner": "wssfk12138", + "url": "https://github.com/wssfk12138/dsh-damage-pulse", + "page": "https://awesome-dsh-plugin.com/p/wssfk12138/dsh-damage-pulse/", + "category": "usage", + "description": { + "en": "Tracks DeepSeek token usage, per-call and session costs, and account balance with cache-aware charge animations in the DSH Web UI.", + "zh": "在 DSH Web 界面追踪 DeepSeek Token 用量、单次与会话费用及账户余额,并显示缓存感知的扣费动画。" + }, + "npm": null, + "stars": 122, + "downloads": null, + "install": "dsh plugin --profile web add github:wssfk12138/dsh-damage-pulse", + "added": "2026-08-23" + }, + { + "name": "dsh-balance-float", + "owner": "x2802490130-prog", + "url": "https://github.com/x2802490130-prog/dsh-balance-float", + "page": "https://awesome-dsh-plugin.com/p/x2802490130-prog/dsh-balance-float/", + "category": "usage", + "description": { + "en": "A floating balance widget for the Web UI: live DeepSeek account balance with manual refresh and a Y/N-confirmed graceful exit.", + "zh": "Web UI 右上角悬浮窗:实时显示 DeepSeek 余额,支持手动刷新与一键优雅退出(Y/N 快捷键确认)。" + }, + "npm": "dsh-balance-float", + "stars": 5, + "downloads": 245, + "install": "dsh plugin --profile web add dsh-balance-float", + "added": "2026-08-15" + }, + { + "name": "dsh-opencode-go-usage", + "owner": "xiaweiliang060035", + "url": "https://github.com/xiaweiliang060035/dsh-opencode-go-usage", + "page": "https://awesome-dsh-plugin.com/p/xiaweiliang060035/dsh-opencode-go-usage/", + "category": "usage", + "description": { + "en": "Floating widget showing real-time OpenCode Go subscription usage (rolling/weekly/monthly) for every API key, with rate-limit alerts and automatic key-pool discovery.", + "zh": "悬浮组件实时显示 OpenCode Go 各 key 用量(滚动/每周/每月),限流预警,自动发现 key 池。" + }, + "npm": "@xiaweiliang060035/dsh-opencode-go-usage", + "stars": 3, + "downloads": 286, + "install": "dsh plugin --profile web add @xiaweiliang060035/dsh-opencode-go-usage", + "added": "2026-08-15" + }, + { + "name": "dsh-rate-limiter", + "owner": "Xidong-AI", + "url": "https://github.com/Xidong-AI/dsh-rate-limiter", + "page": "https://awesome-dsh-plugin.com/p/Xidong-AI/dsh-rate-limiter/", + "category": "usage", + "description": { + "en": "Proactive per-provider token-bucket rate limiting that queues over-limit requests before they are sent, preventing upstream 429 errors.", + "zh": "按 provider 令牌桶主动限速,超限请求在发出前延迟排队而非失败,避免触发上游 429。" + }, + "npm": "@xidong-ai/dsh-rate-limiter", + "stars": 3, + "downloads": 1761, + "install": "dsh plugin --profile web add @xidong-ai/dsh-rate-limiter", + "added": "2026-08-18" + }, + { + "name": "dsh-cpa-status", + "owner": "xohmai", + "url": "https://github.com/xohmai/dsh-cpa-status", + "page": "https://awesome-dsh-plugin.com/p/xohmai/dsh-cpa-status/", + "category": "usage", + "description": { + "en": "CPA account-pool status panel: sidebar health light, quota bars, success-rate ticks, and gateway key endings.", + "zh": "CPA 账号池状态面板:侧栏健康灯、配额进度、成功率刻度与网关密钥末四位一览。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:xohmai/dsh-cpa-status", + "added": "2026-08-20", + "screenshots": [ + "https://raw.githubusercontent.com/xohmai/dsh-cpa-status/main/docs/images/panel.png", + "https://raw.githubusercontent.com/xohmai/dsh-cpa-status/main/docs/images/collapsed.png" + ] + }, + { + "name": "dsh-cost-crystal", + "owner": "xxvk", + "url": "https://github.com/xxvk/dsh-cost-crystal", + "page": "https://awesome-dsh-plugin.com/p/xxvk/dsh-cost-crystal/", + "category": "usage", + "description": { + "en": "Floating cost crystal for the Web UI: balance card, real-time tok/s, peak/off-peak billing countdown, last-24h spend, and a 🔮 next-message cost forecast, all timezone-aware.", + "zh": "Web UI 成本水晶球:余额卡片、实时 tok/s 速率、波峰/低峰计费倒计时、近 24h 消耗,以及 🔮 下一条消息消耗预测,全部时区感知。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:xxvk/dsh-cost-crystal", + "added": "2026-08-16" + }, + { + "name": "dsh-token-usage-sidebar", + "owner": "y2zyyr", + "url": "https://github.com/y2zyyr/dsh-token-usage-sidebar", + "page": "https://awesome-dsh-plugin.com/p/y2zyyr/dsh-token-usage-sidebar/", + "category": "usage", + "description": { + "en": "Persistent provider-reported token usage for DSH Web, with a sidebar summary and settings-page breakdowns by range, token type, provider, and model.", + "zh": "为 DSH Web 持久保存 provider 上报的 Token 用量,提供侧边栏摘要,以及按范围、Token 类别、供应商和模型查看的设置页明细。" + }, + "npm": "@y2zyyr/dsh-token-usage-sidebar", + "stars": 2, + "downloads": 379, + "install": "dsh plugin --profile web add @y2zyyr/dsh-token-usage-sidebar", + "added": "2026-08-18" + }, + { + "name": "dsh-session-report", + "owner": "yangyongzhen", + "url": "https://github.com/yangyongzhen/dsh-session-report", + "page": "https://awesome-dsh-plugin.com/p/yangyongzhen/dsh-session-report/", + "category": "usage", + "description": { + "en": "Per-session cost & usage report cards: tokens, cache hits, duration.", + "zh": "会话成本与耗时报表:tokens / 缓存命中 / 耗时。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:yangyongzhen/dsh-session-report", + "added": "2026-08-15" + }, + { + "name": "dsh-usage-stats", + "owner": "Ychris12138", + "url": "https://github.com/Ychris12138/dsh-usage-stats", + "page": "https://awesome-dsh-plugin.com/p/Ychris12138/dsh-usage-stats/", + "category": "usage", + "description": { + "en": "Multi-provider usage dashboard with provider/model token breakdowns, calendar drill-downs, account balances, and OpenCode Go / Z.ai subscription quota tracking.", + "zh": "多供应商用量看板:按供应商/模型统计 Token 与日期下钻,统一展示账户余额,并追踪 OpenCode Go / Z.ai 订阅额度。" + }, + "npm": "@ychris12138/dsh-usage-stats", + "stars": 123, + "downloads": 1922, + "install": "dsh plugin --profile web add @ychris12138/dsh-usage-stats", + "added": "2026-08-15" + }, + { + "name": "dsh-cost-tracker", + "owner": "yflmq001", + "url": "https://github.com/yflmq001/dsh-cost-tracker", + "page": "https://awesome-dsh-plugin.com/p/yflmq001/dsh-cost-tracker/", + "category": "usage", + "description": { + "en": "Per-model token cost tracking with configurable cache-hit/miss, output and peak-window pricing, a live session cost bar, and unconfigured-model flags.", + "zh": "按模型追踪 token 成本:可配置缓存命中/未命中、输出与高峰时段单价,实时会话花费条,并标记未配置价格的模型。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:yflmq001/dsh-cost-tracker", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/yflmq001/dsh-cost-tracker/HEAD/assets/screenshots/screenshot-main.png", + "https://raw.githubusercontent.com/yflmq001/dsh-cost-tracker/HEAD/assets/screenshots/screenshot-cost-panel.png", + "https://raw.githubusercontent.com/yflmq001/dsh-cost-tracker/HEAD/assets/screenshots/screenshot-config.png" + ] + }, + { + "name": "DSH-Balance-display", + "owner": "yoiizesdev-crypto", + "url": "https://github.com/yoiizesdev-crypto/DSH-Balance-display", + "page": "https://awesome-dsh-plugin.com/p/yoiizesdev-crypto/DSH-Balance-display/", + "category": "usage", + "description": { + "en": "Real-time DeepSeek account balance badge in the sidebar footer with odometer roll animation, multi-currency support, balance trend chart, per-key usage statistics with historical backfill, and GitHub version auto-check.", + "zh": "侧边栏底部实时显示 DeepSeek 账户余额徽章:数字滚轮动画、多币种支持、余额趋势图、按 Key 用量统计(含安装前历史回填),并支持 GitHub 版本自动检查。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:yoiizesdev-crypto/DSH-Balance-display", + "added": "2026-08-19" + }, + { + "name": "dsh-usage-lens", + "owner": "yokesky", + "url": "https://github.com/yokesky/dsh-usage-lens", + "page": "https://awesome-dsh-plugin.com/p/yokesky/dsh-usage-lens/", + "category": "usage", + "description": { + "en": "Usage statistics dashboard for the DSH Web UI: info cards, a 280-day GitHub-style activity heatmap, daily token trend, and model-usage donut chart.", + "zh": "DSH Web UI 用量统计面板:信息卡、280 天活跃热力图、按天 Token 趋势与模型用量双环饼图。" + }, + "npm": "@yokesky/dsh-usage-lens", + "stars": 3, + "downloads": 620, + "install": "dsh plugin --profile web add @yokesky/dsh-usage-lens", + "added": "2026-08-15" + }, + { + "name": "dsh-token-attention", + "owner": "Young4ever33", + "url": "https://github.com/Young4ever33/dsh-token-attention", + "page": "https://awesome-dsh-plugin.com/p/Young4ever33/dsh-token-attention/", + "category": "usage", + "description": { + "en": "Token attention management panel for DeepSeek Harness: per-task/day/week/month token usage and cost tracking (input hit/miss, output, reasoning), DeepSeek peak/off-peak pricing, task-type recognition, and advice on when to switch sessions, write a hand-off, or compact context.", + "zh": "Token 注意力管理面板:按任务/日/周/月记录 token 消耗与费用(命中/未命中/输出/推理四桶),支持 DeepSeek 峰谷计价与任务类型识别,并给出换对话、写 hand-off、压缩上下文的建议。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Young4ever33/dsh-token-attention", + "added": "2026-08-24" + }, + { + "name": "dsh-deepseek-usage", + "owner": "yyb16yyb-hub", + "url": "https://github.com/yyb16yyb-hub/dsh-deepseek-usage", + "page": "https://awesome-dsh-plugin.com/p/yyb16yyb-hub/dsh-deepseek-usage/", + "category": "usage", + "description": { + "en": "Real-time DeepSeek API usage: account balance with low-balance alerts (browser notification on threshold crossing), token & request stats (total/today/last-60s windows, per-model and per-session), estimated cost — a composer dock plus a settings-page usage panel.", + "zh": "实时 DeepSeek API 用量:账户余额与低余额告警(跨阈值浏览器通知)、请求/token 统计(全部/今日/近 60s,按模型与会话下钻)、估算花费——输入框下方统计条 + 设置页用量面板。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:yyb16yyb-hub/dsh-deepseek-usage", + "added": "2026-08-16" + }, + { + "name": "dsh-billing-balance", + "owner": "YZz-S", + "url": "https://github.com/YZz-S/dsh-billing-balance", + "page": "https://awesome-dsh-plugin.com/p/YZz-S/dsh-billing-balance/", + "category": "usage", + "description": { + "en": "Shows DeepSeek API account balance and Volcengine Ark Coding/Agent Plan quota windows (5h/weekly/monthly with reset countdowns) in the settings panel, composer dock, and a draggable floating refresh button.", + "zh": "在设置页、输入框下方读数条与可拖动悬浮按钮三处显示 DeepSeek 官方 API 余额与火山方舟 Coding/Agent Plan 套餐额度(5小时/周/月窗口及重置倒计时)。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:YZz-S/dsh-billing-balance", + "added": "2026-08-15" + }, + { + "name": "dsh-token-cost-meter", + "owner": "YZz-S", + "url": "https://github.com/YZz-S/dsh-token-cost-meter", + "page": "https://awesome-dsh-plugin.com/p/YZz-S/dsh-token-cost-meter/", + "category": "usage", + "description": { + "en": "Real-time session token usage and estimated cost in the composer stats row, with pricing fetched from DeepSeek official pricing page.", + "zh": "在输入框下方统计行实时显示当前会话累计 token 消耗与估算费用(人民币),价格从 DeepSeek 官方价格页动态获取。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:YZz-S/dsh-token-cost-meter", + "added": "2026-08-15" + }, + { + "name": "deepseek-cost-usage-status-plugin", + "owner": "Zenjibad", + "url": "https://github.com/Zenjibad/deepseek-cost-usage-status-plugin", + "page": "https://awesome-dsh-plugin.com/p/Zenjibad/deepseek-cost-usage-status-plugin/", + "category": "usage", + "description": { + "en": "Adds a colored status line under the DSH conversation stats showing live DeepSeek API cost, usage and account balance, with on/off-peak pricing indicators.", + "zh": "在 DSH 会话统计行下方新增彩色状态行,实时显示 DeepSeek API 费用、用量与账户余额,并带高峰/错峰计费指示。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Zenjibad/deepseek-cost-usage-status-plugin", + "added": "2026-08-28" + }, + { + "name": "headroom-stats-plugin", + "owner": "Zenjibad", + "url": "https://github.com/Zenjibad/headroom-stats-plugin", + "page": "https://awesome-dsh-plugin.com/p/Zenjibad/headroom-stats-plugin/", + "category": "usage", + "description": { + "en": "Shows real-time token and cost savings from the Headroom compression proxy in the DSH web UI: a Settings dashboard plus a persistent stats line under the composer.", + "zh": "在 DSH 内实时展示 Headroom 压缩代理的 token 与费用节省:设置页仪表盘 + 输入区常驻统计行。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Zenjibad/headroom-stats-plugin", + "added": "2026-08-20" + }, + { + "name": "llmtrim-stats-plugin", + "owner": "Zenjibad", + "url": "https://github.com/Zenjibad/llmtrim-stats-plugin", + "page": "https://awesome-dsh-plugin.com/p/Zenjibad/llmtrim-stats-plugin/", + "category": "usage", + "description": { + "en": "Live llmtrim savings dashboard in the DSH web UI: Settings page with paid, would-have-cost, saved-today and saved-this-week cards plus per-model breakdown, and a configurable rotating or static stats strip under the composer.", + "zh": "在 DSH 内实时展示 llmtrim 节省统计:设置页金额卡片(已支付、未压缩应付、今日与本周节省)与按模型明细,输入区可配置轮播或静态统计条。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Zenjibad/llmtrim-stats-plugin", + "added": "2026-08-28" + }, + { + "name": "dsh-opencode-go-quota", + "owner": "zer0zio-stack", + "url": "https://github.com/zer0zio-stack/dsh-opencode-go-quota", + "page": "https://awesome-dsh-plugin.com/p/zer0zio-stack/dsh-opencode-go-quota/", + "category": "usage", + "description": { + "en": "Bottom-right 余额 badge for the DSH Web UI: on expand it fetches the currently-selected provider's OpenCode Go plan limits or DeepSeek prepaid balance, plus local token usage and pricing-based spend estimates.", + "zh": "DSH Web 右下角「余额」徽标:展开时读取当前所选提供方,显示 OpenCode Go 计划限额或 DeepSeek 预充值余额,并附本机 token 用量与按定价表估算的消费金额。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:zer0zio-stack/dsh-opencode-go-quota", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/zer0zio-stack/dsh-opencode-go-quota/main/assets/screenshot-deepseek-balance.png", + "https://raw.githubusercontent.com/zer0zio-stack/dsh-opencode-go-quota/main/assets/screenshot-opencode-go-limit.png" + ] + }, + { + "name": "TokenLedger", + "owner": "zh667", + "url": "https://github.com/zh667/TokenLedger", + "page": "https://awesome-dsh-plugin.com/p/zh667/TokenLedger/", + "category": "usage", + "description": { + "en": "Sidebar usage panel that attributes tokens to the relay site that served each request, read from your existing provider config: today/month/all-time totals, per-site and per-model breakdowns, a year activity heatmap, and New API / Sub2API / DeepSeek balances.", + "zh": "侧边栏用量面板:把 Token 归属到实际服务该请求的中转站,站点从已有的 provider 配置中读出,无需额外配置;含今日/本月/累计三窗口、按站点与模型下钻、一年活跃度热力图,以及 New API / Sub2API / DeepSeek 余额。" + }, + "npm": "dsh-tokenledger", + "stars": 157, + "downloads": 1524, + "install": "dsh plugin --profile web add dsh-tokenledger", + "added": "2026-08-15" + }, + { + "name": "dsh-stats-panel", + "owner": "zhang-jiazhi", + "url": "https://github.com/zhang-jiazhi/dsh-stats-panel", + "page": "https://awesome-dsh-plugin.com/p/zhang-jiazhi/dsh-stats-panel/", + "category": "usage", + "description": { + "en": "Token usage statistics for DeepSeek Harness. Per-channel usage with balances and plan quotas for mainstream providers, CNY cost estimates, and daily charts — a settings-sidebar section.", + "zh": "DeepSeek Harness Token 用量统计。按渠道区分统计,官方渠道自动显示余额,套餐渠道显示 5 小时/7 天/30 天用量余量,人民币计费估算,含每日图表与模型分布,集成在设置页侧边菜单。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:zhang-jiazhi/dsh-stats-panel", + "added": "2026-08-17" + }, + { + "name": "dsh-sidebar-balance", + "owner": "zhouchengke2046", + "url": "https://github.com/zhouchengke2046/dsh-sidebar-balance", + "page": "https://awesome-dsh-plugin.com/p/zhouchengke2046/dsh-sidebar-balance/", + "category": "usage", + "description": { + "en": "DeepSeek Harness plugin that shows the DeepSeek API account balance and OpenCode Go plan usage (consumption ring, hover details) in the sidebar footer, with adaptive polling and zh/en locale support.", + "zh": "DSH Web 插件:在侧边栏底部显示 DeepSeek API 余额与 OpenCode Go 套餐用量(消耗圆环、悬浮详情),自适应轮询,支持中英文。" + }, + "npm": "dsh-sidebar-balance", + "stars": 1, + "downloads": 276, + "install": "dsh plugin --profile web add dsh-sidebar-balance", + "added": "2026-08-19" + }, + { + "name": "dsh-save-money#plugin", + "owner": "zhu168", + "url": "https://github.com/zhu168/dsh-save-money/tree/main/plugin", + "page": "https://awesome-dsh-plugin.com/p/zhu168/dsh-save-money--plugin/", + "category": "usage", + "description": { + "en": "Pause and resume tasks automatically during user-defined time windows to avoid peak-hour API costs; includes account balance display and 10-minute granularity spend bar chart.", + "zh": "自定义时间窗口自动暂停/恢复任务,高峰时段节省 API 费用;含账户余额显示和 10 分钟粒度的消费柱状图。" + }, + "npm": "dsh-save-money", + "stars": 33, + "downloads": 795, + "install": "dsh plugin --profile web add dsh-save-money", + "added": "2026-08-18" + }, + { + "name": "dsh-cost-balance", + "owner": "zoumutou", + "url": "https://github.com/zoumutou/dsh-cost-balance", + "page": "https://awesome-dsh-plugin.com/p/zoumutou/dsh-cost-balance/", + "category": "usage", + "description": { + "en": "Collapsible iOS-style stats pill under the composer: session cost, DeepSeek account balance, cache-hit rate, and token usage in a frosted panel.", + "zh": "输入框下方的 iOS 风格统计条:一键展开查看会话花费、DeepSeek 账户余额、缓存命中率与 Token 用量。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:zoumutou/dsh-cost-balance", + "added": "2026-08-14" + }, + { + "name": "dsh-neo-skin", + "owner": "0nt-one", + "url": "https://github.com/0nt-one/dsh-neo-skin", + "page": "https://awesome-dsh-plugin.com/p/0nt-one/dsh-neo-skin/", + "category": "theme", + "description": { + "en": "Neo-brutalism skin with two switchable schemes (Blue Command / Aged Newspaper), hard shadows, sharp corners, and light/dark theme support.", + "zh": "新粗野主义换肤皮肤:双方案(蓝统治 / 做旧报纸)随时切换,硬阴影 + 直角 + 2px 边框结构层,浅色/深色自适应。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:0nt-one/dsh-neo-skin", + "added": "2026-08-16" + }, + { + "name": "dsh-models-dev-reasoning", + "owner": "aerince", + "url": "https://github.com/aerince/dsh-models-dev-reasoning", + "page": "https://awesome-dsh-plugin.com/p/aerince/dsh-models-dev-reasoning/", + "category": "theme", + "description": { + "en": "Adds models.dev reasoning levels to unconfigured third-party DeepSeek Harness models.", + "zh": "为未配置的第三方 DeepSeek Harness 模型添加 models.dev 推理级别支持。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:aerince/dsh-models-dev-reasoning", + "added": "2026-08-15" + }, + { + "name": "dsh-cyber-particle", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/dsh-cyber-particle", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/dsh-cyber-particle/", + "category": "theme", + "description": { + "en": "Particle-network background overlay for the DSH Web shell: full-screen, click-through, zero runtime dependencies.", + "zh": "为 DSH Web 界面提供粒子网络动态背景:全屏覆盖、点击穿透、零运行时依赖。" + }, + "npm": null, + "stars": 11, + "downloads": null, + "install": "dsh plugin --profile web add github:AKS1st/dsh-cyber-particle", + "added": "2026-08-15" + }, + { + "name": "ikun-theme-skin", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/ikun-theme-skin", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/ikun-theme-skin/", + "category": "theme", + "description": { + "en": "IKUN fandom skin for the DSH Web UI: star-blue and black-gold palettes in the system theme list, full-screen photo wallpaper rotation, a music box, and a send-button voice line.", + "zh": "ikun 主题皮肤:星蓝昼/夜与背带裤黑金三套配色接入系统主题列表,全屏照片壁纸轮播、音乐盒与发送音效。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:AKS1st/ikun-theme-skin", + "added": "2026-08-15" + }, + { + "name": "deepseek-harness-hello-kitty-suite", + "owner": "Angel2518975237", + "url": "https://github.com/Angel2518975237/deepseek-harness-hello-kitty-suite", + "page": "https://awesome-dsh-plugin.com/p/Angel2518975237/deepseek-harness-hello-kitty-suite/", + "category": "theme", + "description": { + "en": "Hello Kitty-styled task-completion and question alerts for DSH Web, bundled with an optional light/dark pink Skin Center v2 theme and local install scripts.", + "zh": "DSH Web 的 Hello Kitty 风格任务完成与待回答提醒插件,附带可选的粉色明暗双主题 Skin Center v2 皮肤及本地安装脚本。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:Angel2518975237/deepseek-harness-hello-kitty-suite", + "added": "2026-08-24", + "screenshots": [ + "https://raw.githubusercontent.com/Angel2518975237/deepseek-harness-hello-kitty-suite/HEAD/docs/screenshots/preview.png", + "https://raw.githubusercontent.com/Angel2518975237/deepseek-harness-hello-kitty-suite/HEAD/docs/screenshots/preview-toast.png" + ] + }, + { + "name": "dsh-custom-brand", + "owner": "baisama-cloud", + "url": "https://github.com/baisama-cloud/dsh-custom-brand", + "page": "https://awesome-dsh-plugin.com/p/baisama-cloud/dsh-custom-brand/", + "category": "theme", + "description": { + "en": "Customizable brand area for the Web UI: replace the whale logo and DeepSeek wordmark with local images, and edit the HARNESS badge text (double-click to change, right-click to reset).", + "zh": "Web UI 品牌区自定义:鲸鱼 logo 与 DeepSeek 文字可换成本地图片,HARNESS 徽章文字可双击编辑(双击修改,右键恢复)。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:baisama-cloud/dsh-custom-brand", + "added": "2026-08-15" + }, + { + "name": "dsh-theme-plugin", + "owner": "BeiZi6", + "url": "https://github.com/BeiZi6/dsh-theme-plugin", + "page": "https://awesome-dsh-plugin.com/p/BeiZi6/dsh-theme-plugin/", + "category": "theme", + "description": { + "en": "Theme studio for the DSH Web GUI: five built-in presets plus fully customizable light/dark palettes (accent, background, foreground, UI and code fonts, translucent sidebar, contrast), hot-swapped instantly and persisted in localStorage.", + "zh": "DSH Web GUI 主题工作室:5 套内置预设 + 完全可自定义的浅/深配色(强调色、背景、前景、UI 与代码字体、半透明侧栏、对比度),即时热切换并持久化到 localStorage。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:BeiZi6/dsh-theme-plugin", + "added": "2026-08-15" + }, + { + "name": "dsh-client-ui-skins", + "owner": "caoyiwei850", + "url": "https://github.com/caoyiwei850/dsh-client-ui-skins", + "page": "https://awesome-dsh-plugin.com/p/caoyiwei850/dsh-client-ui-skins/", + "category": "theme", + "description": { + "en": "Skin plugin for the DSH Web UI: 4 built-in skins plus custom image skins with the photo as a full-interface background and the palette following its dominant hue.", + "zh": "DSH Web 换肤插件:4 套内置皮肤 + 自定义图片皮肤,图片作为全界面背景,配色自动跟随图片主色调。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:caoyiwei850/dsh-client-ui-skins", + "added": "2026-08-15" + }, + { + "name": "dsh-wallpaper", + "owner": "chinaRXQ", + "url": "https://github.com/chinaRXQ/dsh-wallpaper", + "page": "https://awesome-dsh-plugin.com/p/chinaRXQ/dsh-wallpaper/", + "category": "theme", + "description": { + "en": "Wallpaper skin for the DSH Web UI: image background with opacity, mask and blur controls.", + "zh": "DSH Web 壁纸皮肤:图片背景,可调透明度、压暗遮罩与模糊。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:chinaRXQ/dsh-wallpaper", + "added": "2026-08-15" + }, + { + "name": "dsh-uiskin-theme", + "owner": "chouxiaohuai", + "url": "https://github.com/chouxiaohuai/dsh-uiskin-theme", + "page": "https://awesome-dsh-plugin.com/p/chouxiaohuai/dsh-uiskin-theme/", + "category": "theme", + "description": { + "en": "A Blue Glass ocean-fantasy skin for DeepSeek Harness Web: crisp ocean backdrop, glass bubbles, an ocean sidebar with a gold-gradient HARNESS wordmark and a bottom cartoon character, a whale settings button, and gradient model text.", + "zh": "DeepSeek Harness Web 的海洋幻想玻璃皮肤:清澈海洋背景、玻璃气泡、海洋侧边栏(金色渐变「HARNESS」字标 + 底部卡通角色)、鲸鱼设置按钮、渐变模型文字。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:chouxiaohuai/dsh-uiskin-theme", + "added": "2026-08-24" + }, + { + "name": "dsh-wallpaper", + "owner": "code-newbie-1", + "url": "https://github.com/code-newbie-1/dsh-wallpaper", + "page": "https://awesome-dsh-plugin.com/p/code-newbie-1/dsh-wallpaper/", + "category": "theme", + "description": { + "en": "Wallpaper Engine integration for the dsh web GUI: scan the local WE library (Steam workshop 431960 + local projects) and use its wallpapers as the page background — image/video/web rendered natively, scene wallpapers as softened static previews — with a wallpaper-design panel (opacity, scope, blur, vignette, fps, parallax, carousel, theme linkage) and agent tools wallpaper_scan/list/set/config.", + "zh": "Wallpaper Engine 壁纸联动:把本机下载的 WE 壁纸(创意工坊 431960 + 本地 projects)设为 DSH Web GUI 页面背景(图片/视频/网页原样渲染,场景壁纸柔化为静态预览),侧边栏「壁纸设计」面板可调不透明度、作用范围、填充、模糊、暗角、帧率、视差、轮播与主题联动,附 wallpaper_scan/list/set/config 四个 agent 工具。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:code-newbie-1/dsh-wallpaper", + "added": "2026-08-18" + }, + { + "name": "dsh-wallpaper", + "owner": "codeMonkey-Pine", + "url": "https://github.com/codeMonkey-Pine/dsh-wallpaper", + "page": "https://awesome-dsh-plugin.com/p/codeMonkey-Pine/dsh-wallpaper/", + "category": "theme", + "description": { + "en": "Wallpaper Engine integration for the dsh web GUI: scan the local WE library (Steam workshop 431960 + local projects) and use its wallpapers as the page background — image/video/web rendered natively, scene wallpapers as softened static previews — with a 壁纸设计 panel (opacity, scope, fill, blur, vignette, fps, parallax, carousel, theme linkage) and agent tools wallpaper_scan/list/set/config.", + "zh": "Wallpaper Engine 壁纸联动:把本机下载的 WE 壁纸(创意工坊 431960 + 本地 projects)设为 DSH Web GUI 页面背景(图片/视频/网页原样渲染,场景壁纸柔化为静态预览),侧边栏「壁纸设计」面板可调不透明度、作用范围、填充、模糊、暗角、帧率、视差、轮播与主题联动,附 wallpaper_scan/list/set/config 四个 agent 工具。" + }, + "npm": "dsh-wallpaper", + "stars": 3, + "downloads": 852, + "install": "dsh plugin --profile web add dsh-wallpaper", + "added": "2026-08-19" + }, + { + "name": "open-sea-skin", + "owner": "d-dev0101", + "url": "https://github.com/d-dev0101/open-sea-skin", + "page": "https://awesome-dsh-plugin.com/p/d-dev0101/open-sea-skin/", + "category": "theme", + "description": { + "en": "Realtime WebGPU ocean skin with quick controls for waves, daylight, glass opacity, and an automatic day cycle.", + "zh": "实时 WebGPU 海洋皮肤,可快捷调节波浪、日光、玻璃不透明度与自动昼夜循环。" + }, + "npm": null, + "tarball": "https://github.com/d-dev0101/open-sea-skin/releases/download/v1.2.0/open-sea-skin-1.2.0.tgz", + "stars": 192, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/d-dev0101/open-sea-skin/releases/download/v1.2.0/open-sea-skin-1.2.0.tgz\"", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/d-dev0101/open-sea-skin/5ae54402ec6d4c961d41aee8ed786280799712c7/docs/marketplace/open-sea-harness-cover.png", + "https://raw.githubusercontent.com/d-dev0101/open-sea-skin/5ae54402ec6d4c961d41aee8ed786280799712c7/docs/screenshots/harness-dark-overview-40.gif", + "https://raw.githubusercontent.com/d-dev0101/open-sea-skin/5ae54402ec6d4c961d41aee8ed786280799712c7/docs/screenshots/harness-light-overview-40.gif", + "https://raw.githubusercontent.com/d-dev0101/open-sea-skin/5ae54402ec6d4c961d41aee8ed786280799712c7/docs/screenshots/harness-wave-control-40.gif", + "https://raw.githubusercontent.com/d-dev0101/open-sea-skin/5ae54402ec6d4c961d41aee8ed786280799712c7/docs/screenshots/harness-daylight-sunset-40.gif" + ] + }, + { + "name": "dsh-web-ui#dsh-skins", + "owner": "DamonKoy", + "url": "https://github.com/DamonKoy/dsh-web-ui/tree/main/packages/dsh-skins", + "page": "https://awesome-dsh-plugin.com/p/DamonKoy/dsh-web-ui--packages-dsh-skins/", + "category": "theme", + "description": { + "en": "Skin family aggregator: one install gives the skin center plus all skin assets (qq98, ths, xp, blue-fantasy, dragon-heir, minecraft, miku, trading).", + "zh": "皮肤全家桶聚合插件:一键安装皮肤中心 + 全部皮肤资产(qq98/ths/xp/blue-fantasy/dragon-heir/minecraft/miku/trading 等)。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:DamonKoy/dsh-web-ui#path:/packages/dsh-skins", + "added": "2026-08-17" + }, + { + "name": "dsh-shunshun-wallpaper", + "owner": "Ddamage", + "url": "https://github.com/Ddamage/dsh-shunshun-wallpaper", + "page": "https://awesome-dsh-plugin.com/p/Ddamage/dsh-shunshun-wallpaper/", + "category": "theme", + "description": { + "en": "Fan-made wallpaper plugin themed on the streamer Zhang Shunfei: full-screen high-definition rotating colorful pegasus GIF wallpaper with theme blending and prominence control, plus a Bilibili music player (add songs by pasting a link, delete, volume, seek, single/list/shuffle loop, and a sidebar mini player).", + "zh": "以网络主播张顺飞为主题的粉丝同人二创壁纸插件:彩色飞马高清旋转动图全屏壁纸,支持主题融合与突出度调节;附带 B站音乐播放器,可粘贴链接添加歌曲、删除、调音量、拖进度条、切换单曲/列表/随机循环,并有侧栏迷你播放器。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Ddamage/dsh-shunshun-wallpaper", + "added": "2026-08-27" + }, + { + "name": "dsh-wallpaper-skin", + "owner": "ddbj-hub", + "url": "https://github.com/ddbj-hub/dsh-wallpaper-skin", + "page": "https://awesome-dsh-plugin.com/p/ddbj-hub/dsh-wallpaper-skin/", + "category": "theme", + "description": { + "en": "Persistent wallpaper skin for the dsh web profile: static image or muted looping video as the app background, with translucent panels and a floating picker panel.", + "zh": "dsh Web 界面的持久化壁纸皮肤:静态图片或静音循环视频作为应用背景,面板半透明化,右下角悬浮面板一键换壁纸。" + }, + "npm": "dsh-wallpaper-skin", + "stars": 2, + "downloads": 206, + "install": "dsh plugin --profile web add dsh-wallpaper-skin", + "added": "2026-08-17" + }, + { + "name": "dsh-theme-mineradio", + "owner": "dhicoc", + "url": "https://github.com/dhicoc/dsh-theme-mineradio", + "page": "https://awesome-dsh-plugin.com/p/dhicoc/dsh-theme-mineradio/", + "category": "theme", + "description": { + "en": "Mineradio glassmorphism theme for the DSH web UI: champagne-gold glass panes, a fluid or wallpaper backdrop, and a one-switch toggle that restores the stock look exactly.", + "zh": "Mineradio 玻璃拟态 DSH 网页主题:香槟金玻璃面板、流体或壁纸背景,一键开关即可还原原生界面。" + }, + "npm": "dsh-theme-mineradio", + "stars": 2, + "downloads": 1637, + "install": "dsh plugin --profile web add dsh-theme-mineradio", + "added": "2026-08-20" + }, + { + "name": "dsh-arknights#pramanix-eyjafjalla", + "owner": "DocJlm", + "url": "https://github.com/DocJlm/dsh-arknights/tree/main/skins/pramanix-eyjafjalla", + "page": "https://awesome-dsh-plugin.com/p/DocJlm/dsh-arknights--skins-pramanix-eyjafjalla/", + "category": "theme", + "description": { + "en": "Arknights-inspired fan skin for the DSH Web UI featuring Pramanix and Eyjafjalla in a day/night celestial garden.", + "zh": "DSH Web 明日方舟同人皮肤“初雪和小羊”,包含昼夜星海庭园背景与双角色布局。" + }, + "npm": "dsh-arknights", + "stars": 3, + "downloads": 208, + "install": "dsh plugin --profile web add dsh-arknights", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/DocJlm/dsh-arknights/HEAD/skins/pramanix-eyjafjalla/preview/cover.webp" + ] + }, + { + "name": "dsh-thought-buddy", + "owner": "dsh-plugins", + "url": "https://github.com/dsh-plugins/dsh-thought-buddy", + "page": "https://awesome-dsh-plugin.com/p/dsh-plugins/dsh-thought-buddy/", + "category": "theme", + "description": { + "en": "A GrokBot-style animated avatar with a synchronized typewriter status line — right in front of the \"Deep diving...\" indicator.", + "zh": "在「Deep diving...」状态提示前,放一只动态小伙伴——GrokBot 风格动画头像,状态文字还会同步打字机变换。" + }, + "npm": "@dsh-plugin/dsh-thought-buddy", + "stars": 4, + "downloads": 1729, + "install": "dsh plugin --profile web add @dsh-plugin/dsh-thought-buddy", + "added": "2026-08-16" + }, + { + "name": "dsh-wallpaper-engine", + "owner": "elysia395", + "url": "https://github.com/elysia395/dsh-wallpaper-engine", + "page": "https://awesome-dsh-plugin.com/p/elysia395/dsh-wallpaper-engine/", + "category": "theme", + "description": { + "en": "Wallpaper Engine backgrounds for the DSH web GUI: plays local Video/Web wallpapers behind the chat, renders Scene wallpapers as extracted static frames, and adds content-rating/type filters, custom uploads and an iOS-style liquid-glass settings window.", + "zh": "DSH 网页界面壁纸:把本机 Wallpaper Engine 的 Video/Web 壁纸实时播放到聊天界面后方,Scene 壁纸提取主纹理作为静态帧,支持内容分级/类型过滤、自定义壁纸上传与 iOS 液态玻璃设置窗口。" + }, + "npm": "dsh-plugin-wallpaper-engine", + "stars": 191, + "downloads": 7489, + "install": "dsh plugin --profile web add dsh-plugin-wallpaper-engine", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/elysia395/dsh-wallpaper-engine/main/docs/images/showcase.png", + "https://raw.githubusercontent.com/elysia395/dsh-wallpaper-engine/main/docs/images/wallpaper-library.png", + "https://raw.githubusercontent.com/elysia395/dsh-wallpaper-engine/main/docs/images/liquid-glass-window.png", + "https://raw.githubusercontent.com/elysia395/dsh-wallpaper-engine/main/docs/images/features.png", + "https://raw.githubusercontent.com/elysia395/dsh-wallpaper-engine/main/docs/images/compact-wallpaper-library.png", + "https://raw.githubusercontent.com/elysia395/dsh-wallpaper-engine/main/docs/images/better-sidebar.png" + ] + }, + { + "name": "dsh-theme-eink-retro", + "owner": "exoticknight", + "url": "https://github.com/exoticknight/dsh-theme-eink-retro", + "page": "https://awesome-dsh-plugin.com/p/exoticknight/dsh-theme-eink-retro/", + "category": "theme", + "description": { + "en": "A paper-and-ink client-side theme for the DeepSeek Harness Web UI: Balanced preserves semantic status colors, and Immersive maps supported surfaces to monochrome.", + "zh": "DeepSeek Harness Web UI 纸墨风客户端主题,提供保留语义状态色的平衡模式和黑白沉浸模式。" + }, + "npm": null, + "tarball": "https://github.com/exoticknight/dsh-theme-eink-retro/releases/download/v0.1.0/dsh-theme-eink-retro-0.1.0.tgz", + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/exoticknight/dsh-theme-eink-retro/releases/download/v0.1.0/dsh-theme-eink-retro-0.1.0.tgz\"", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/exoticknight/dsh-theme-eink-retro/HEAD/assets/screenshots/balanced-fresh-profile.png", + "https://raw.githubusercontent.com/exoticknight/dsh-theme-eink-retro/HEAD/assets/screenshots/theme-settings-fresh-profile.png" + ] + }, + { + "name": "dsh-theme-liquid-glass", + "owner": "FAVKTOXIC", + "url": "https://github.com/FAVKTOXIC/dsh-theme-liquid-glass", + "page": "https://awesome-dsh-plugin.com/p/FAVKTOXIC/dsh-theme-liquid-glass/", + "category": "theme", + "description": { + "en": "Genuine Liquid Glass theme for DSH Web UI: SVG feDisplacementMap edge refraction on input card, send button, bubbles, view tabs, queue dock and sidebar. Built-in animated demo wallpaper, customizable background blur & brightness, redesigned model selector popup, glass-styled buttons throughout, and a unique water-drop settings icon.", + "zh": "真正的液态玻璃 DSH 主题:SVG 边缘折射效果覆盖输入框、发送按钮、消息气泡、视图标签、队列坞和侧边栏。自带动态色块壁纸,可自定义背景模糊与亮度,重新设计的模型选择弹窗,全界面玻璃质感按钮,以及水滴形状的专属设置图标。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:FAVKTOXIC/dsh-theme-liquid-glass", + "added": "2026-08-27" + }, + { + "name": "dsh-opencode-palette", + "owner": "FeatherHunter", + "url": "https://github.com/FeatherHunter/dsh-opencode-palette", + "page": "https://awesome-dsh-plugin.com/p/FeatherHunter/dsh-opencode-palette/", + "category": "theme", + "description": { + "en": "Ports all 34 opencode official themes (33 static JSON + 1 system) into DeepSeek Harness: data-driven pipeline (JSON → color resolve → DSH override), per-browser persistence, monospace/proportional choice, five code fonts, and a system fallback that keeps typography only.", + "zh": "把 opencode 官方 34 款主题(33 个静态 JSON + system)完整搬到 DeepSeek Harness:数据驱动三层管线(JSON → 颜色解析 → DSH 覆盖注入),按浏览器持久化,等宽/正体切换、五种代码字体实时预览,system 模式仅保留排印。" + }, + "npm": null, + "tarball": "https://github.com/FeatherHunter/dsh-opencode-palette/releases/download/v1.6.4/dsh-opencode-palette-1.6.4.tgz", + "stars": 16, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/FeatherHunter/dsh-opencode-palette/releases/download/v1.6.4/dsh-opencode-palette-1.6.4.tgz\"", + "added": "2026-08-18" + }, + { + "name": "dsh-thirteen-bg", + "owner": "feng78-boop", + "url": "https://github.com/feng78-boop/dsh-thirteen-bg", + "page": "https://awesome-dsh-plugin.com/p/feng78-boop/dsh-thirteen-bg/", + "category": "theme", + "description": { + "en": "Animated and video live wallpaper for the DeepSeek Harness Web GUI — GIF, animated WebP/APNG and static PNG/JPEG images, plus MP4/WebM video backgrounds, with auto format detection, a dim slider, and UI chrome tone matching.", + "zh": "DeepSeek Harness Web 界面动态壁纸插件:支持 GIF / 动图 WebP / APNG 与静态 PNG/JPG 图片,以及 MP4 / WebM 视频背景,格式自动识别,自带压暗与亮度调节,可让界面色调跟随背景。" + }, + "npm": "dsh-thirteen-bg", + "stars": 1, + "downloads": 2434, + "install": "dsh plugin --profile web add dsh-thirteen-bg", + "added": "2026-08-20", + "screenshots": [ + "https://raw.githubusercontent.com/feng78-boop/dsh-thirteen-bg/master/assets/demo-poster.jpg", + "https://raw.githubusercontent.com/feng78-boop/dsh-thirteen-bg/master/assets/demo-shot-2.jpg" + ] + }, + { + "name": "deep-whale-day-night-theme", + "owner": "GGBond2424648901", + "url": "https://github.com/GGBond2424648901/deep-whale-day-night-theme", + "page": "https://awesome-dsh-plugin.com/p/GGBond2424648901/deep-whale-day-night-theme/", + "category": "theme", + "description": { + "en": "Non-commercial day/night whale-girl skin for official Harness rc.7: current no-viewport-frame layout, paired crystal-workshop and moon-tide scenes, chibi companions, translucent ornaments, and lightweight ambience.", + "zh": "面向官方 Harness rc.7 的非商业鲸鱼娘昼夜皮肤:当前无整屏边框布局、白昼水晶工坊与夜晚月潮观测室双场景、Q 版侧栏伙伴、透明花边与轻量氛围。" + }, + "npm": null, + "tarball": "https://github.com/GGBond2424648901/deep-whale-day-night-theme/releases/download/v0.1.12/deep-whale-day-night-theme-0.1.12.tgz", + "stars": 106, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/GGBond2424648901/deep-whale-day-night-theme/releases/download/v0.1.12/deep-whale-day-night-theme-0.1.12.tgz\"", + "added": "2026-08-16" + }, + { + "name": "dsh-stylevault", + "owner": "GptsApp", + "url": "https://github.com/GptsApp/dsh-stylevault", + "page": "https://awesome-dsh-plugin.com/p/GptsApp/dsh-stylevault/", + "category": "theme", + "description": { + "en": "30 classic open-source theme palettes (Catppuccin, Nord, Tokyo Night, Gruvbox, Solarized, etc.) mapped to official ThemeService tokens for DeepSeek Harness, with a Style Settings panel for live color/font/radius tweaks and JSON export/import for sharing config packs.", + "zh": "为 DeepSeek Harness 提供 30 套经典开源主题配色(Catppuccin、Nord、Tokyo Night、Gruvbox、Solarized 等),映射官方 ThemeService token;带 Style Settings 面板,可实时调整颜色/字体/圆角,并支持 JSON 导出/导入分享配置。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:GptsApp/dsh-stylevault", + "added": "2026-08-18" + }, + { + "name": "dsh-moonrise", + "owner": "hanyi7867069-create", + "url": "https://github.com/hanyi7867069-create/dsh-moonrise", + "page": "https://awesome-dsh-plugin.com/p/hanyi7867069-create/dsh-moonrise/", + "category": "theme", + "description": { + "en": "Moonrise dark theme for the DSH Web UI, with deep indigo surfaces and warm amber accents.", + "zh": "月升暗色主题——深蓝夜色打底、琥珀月光点缀。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:hanyi7867069-create/dsh-moonrise", + "added": "2026-08-18" + }, + { + "name": "deepseek-harness-background", + "owner": "HaoyueQin", + "url": "https://github.com/HaoyueQin/deepseek-harness-background", + "page": "https://awesome-dsh-plugin.com/p/HaoyueQin/deepseek-harness-background/", + "category": "theme", + "description": { + "en": "Custom background image for the DSH Web UI: upload a picture or paste an image URL behind the whole app surface, with opacity, theme-aware scrim, panel transparency, frosted-glass and wallpaper blur, live preview and persisted settings, plus an optional DeepSeek-style timeline rail with per-question bookmarks for long conversations.", + "zh": "DSH Web 自定义背景插件:上传本地图片或粘贴图片链接作为全界面背景,可调不透明度、明暗自适应遮罩、面板透明度、毛玻璃与壁纸模糊,实时预览、设置持久化;另附可选的 DeepSeek 官网风格会话时间线导航轨,支持重点书签与快速跳转。" + }, + "npm": "deepseek-harness-background", + "stars": 1, + "downloads": 930, + "install": "dsh plugin --profile web add deepseek-harness-background", + "added": "2026-08-22", + "screenshots": [ + "https://raw.githubusercontent.com/HaoyueQin/deepseek-harness-background/HEAD/docs/images/home.jpg", + "https://raw.githubusercontent.com/HaoyueQin/deepseek-harness-background/HEAD/docs/images/conversation.jpg", + "https://raw.githubusercontent.com/HaoyueQin/deepseek-harness-background/HEAD/docs/images/settings.jpg" + ] + }, + { + "name": "dsh-anthropic-fonts", + "owner": "Isilsolme", + "url": "https://github.com/Isilsolme/dsh-anthropic-fonts", + "page": "https://awesome-dsh-plugin.com/p/Isilsolme/dsh-anthropic-fonts/", + "category": "theme", + "description": { + "en": "Anthropic Sans, Serif, and Mono fonts for the DSH Web UI, with Source Han fallback for CJK.", + "zh": "Anthropic Sans/Serif/Mono 字体:界面 Sans、对话 Serif、代码 Mono,中文回退思源字体。" + }, + "npm": "dsh-anthropic-fonts", + "stars": 2, + "downloads": 616, + "install": "dsh plugin --profile web add dsh-anthropic-fonts", + "added": "2026-08-15" + }, + { + "name": "dsh-theme-blackhole", + "owner": "jiangwangyang", + "url": "https://github.com/jiangwangyang/dsh-theme-blackhole", + "page": "https://awesome-dsh-plugin.com/p/jiangwangyang/dsh-theme-blackhole/", + "category": "theme", + "description": { + "en": "A black hole theme plugin for the DeepSeek Harness (dsh) Web UI.", + "zh": "DeepSeek Harness(dsh)Web UI 的黑洞主题插件。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:jiangwangyang/dsh-theme-blackhole", + "added": "2026-08-22" + }, + { + "name": "dsh-official-homepage-theme", + "owner": "JohnnyTing", + "url": "https://github.com/JohnnyTing/dsh-official-homepage-theme", + "page": "https://awesome-dsh-plugin.com/p/JohnnyTing/dsh-official-homepage-theme/", + "category": "theme", + "description": { + "en": "A web theme recreating the DeepSeek Harness homepage with a fluid background, an elastic pointer grid, animated fish, and adjustable effects.", + "zh": "复刻 DeepSeek Harness 首页视觉的 Web 主题,提供流体背景、鼠标弹性网格、动态小鱼与可调节效果。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:JohnnyTing/dsh-official-homepage-theme", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/JohnnyTing/dsh-official-homepage-theme/HEAD/static/preview.gif", + "https://raw.githubusercontent.com/JohnnyTing/dsh-official-homepage-theme/HEAD/static/preview.png", + "https://raw.githubusercontent.com/JohnnyTing/dsh-official-homepage-theme/HEAD/static/settings.png" + ] + }, + { + "name": "dsh-custom-theme-import", + "owner": "Juryorca", + "url": "https://github.com/Juryorca/dsh-custom-theme-import", + "page": "https://awesome-dsh-plugin.com/p/Juryorca/dsh-custom-theme-import/", + "category": "theme", + "description": { + "en": "DSH Web skin manager for mainstream DSH skin packages: import local paths or GitHub collections, scan installed skins from the web profile, preview/use/disable/refresh/delete, with host-side persistence.", + "zh": "DSH Web 皮肤管理器:支持主流 DSH 皮肤包本地/GitHub 导入、扫描已安装皮肤、预览/使用/禁用/刷新/删除,宿主持久化。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Juryorca/dsh-custom-theme-import", + "added": "2026-08-18" + }, + { + "name": "dsh-wallpaper", + "owner": "keke050", + "url": "https://github.com/keke050/dsh-wallpaper", + "page": "https://awesome-dsh-plugin.com/p/keke050/dsh-wallpaper/", + "category": "theme", + "description": { + "en": "Wallpaper skin for the DSH Web UI: presets, image URL or upload, and an opacity slider that fades the interface to reveal the wallpaper.", + "zh": "DSH Web 壁纸皮肤:预设 / 图片 URL / 本地上传,透明度滑块让整面界面透出壁纸。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:keke050/dsh-wallpaper", + "added": "2026-08-15" + }, + { + "name": "dsh-skin", + "owner": "KinGao294", + "url": "https://github.com/KinGao294/dsh-skin", + "page": "https://awesome-dsh-plugin.com/p/KinGao294/dsh-skin/", + "category": "theme", + "description": { + "en": "Codex-style skin switcher plus a custom wallpaper layer with opacity and blur controls.", + "zh": "Codex 风格皮肤切换器 + 自定义壁纸层,可调透明度与模糊。" + }, + "npm": null, + "stars": 19, + "downloads": null, + "install": "dsh plugin --profile web add github:KinGao294/dsh-skin", + "added": "2026-08-14" + }, + { + "name": "dsh-liang-skin", + "owner": "kingOfSoySauce", + "url": "https://github.com/kingOfSoySauce/dsh-liang-skin", + "page": "https://awesome-dsh-plugin.com/p/kingOfSoySauce/dsh-liang-skin/", + "category": "theme", + "description": { + "en": "Adaptive reasoning slider skin that maps each model's available reasoning efforts onto a 0–30 visual intensity scale, with synchronized portraits, background, and interface colors.", + "zh": "自适应推理等级滑块皮肤,将当前模型可用档位映射到 0–30 视觉强度,并同步人物、背景和界面配色。" + }, + "npm": "dsh-client-liang-intensity-skin", + "tarball": "https://github.com/kingOfSoySauce/dsh-liang-skin/releases/download/v0.1.4/dsh-client-liang-intensity-skin-0.1.4.tgz", + "stars": 139, + "downloads": 587, + "install": "dsh plugin --profile web add dsh-client-liang-intensity-skin", + "added": "2026-08-17" + }, + { + "name": "awesome-dsh-background-plugin", + "owner": "leavestring", + "url": "https://github.com/leavestring/awesome-dsh-background-plugin", + "page": "https://awesome-dsh-plugin.com/p/leavestring/awesome-dsh-background-plugin/", + "category": "theme", + "description": { + "en": "DSH Web background settings: upload a local image or pick a preset ambiance, with live preview and persistent settings.", + "zh": "为 DSH Web 设置背景:上传本地图片或选择预设氛围,实时预览且设置持久化。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:leavestring/awesome-dsh-background-plugin", + "added": "2026-08-16" + }, + { + "name": "dsh-qq2007-skin", + "owner": "LeemanCheung", + "url": "https://github.com/LeemanCheung/dsh-qq2007-skin", + "page": "https://awesome-dsh-plugin.com/p/LeemanCheung/dsh-qq2007-skin/", + "category": "theme", + "description": { + "en": "QQ 2007-inspired DSH Web skin: 72 native theme tokens, scoped three-pane chrome, original offline art and pixel buddy, an optional synthesized send chime, responsive/accessibility fallbacks, and reversible Settings switches.", + "zh": "DSH Web GUI 的 QQ 2007 风格皮肤:72 个原生主题 token、作用域三栏窗框、原创离线素材与像素伙伴、可选合成发送提示音、响应式/无障碍回退和可恢复设置开关。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:LeemanCheung/dsh-qq2007-skin", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/LeemanCheung/dsh-qq2007-skin/main/docs/screenshot.png", + "https://raw.githubusercontent.com/LeemanCheung/dsh-qq2007-skin/main/docs/preview.svg" + ] + }, + { + "name": "dsh-taffy-theme", + "owner": "lengzhanbao", + "url": "https://github.com/lengzhanbao/dsh-taffy-theme", + "page": "https://awesome-dsh-plugin.com/p/lengzhanbao/dsh-taffy-theme/", + "category": "theme", + "description": { + "en": "Candy-pink acrylic DSH Web theme with light conservatory and dark stage art, gold-pink chat frame, Taffy character overlays, and an optional agent preset.", + "zh": "粉金亚克力 DSH Web 主题,含浅色花房与深色舞台背景、粉金对话框、塔菲立绘装饰与可选 Agent 预设。" + }, + "npm": null, + "tarball": "https://github.com/lengzhanbao/dsh-taffy-theme/releases/latest/download/dsh-external-dsh-taffy-theme-0.1.2.tgz", + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/lengzhanbao/dsh-taffy-theme/releases/latest/download/dsh-external-dsh-taffy-theme-0.1.2.tgz\"", + "added": "2026-08-23", + "screenshots": [ + "https://raw.githubusercontent.com/lengzhanbao/dsh-taffy-theme/master/preview/light.webp", + "https://raw.githubusercontent.com/lengzhanbao/dsh-taffy-theme/master/preview/dark.webp" + ] + }, + { + "name": "dsh-neu-theme", + "owner": "Lhy723", + "url": "https://github.com/Lhy723/dsh-neu-theme", + "page": "https://awesome-dsh-plugin.com/p/Lhy723/dsh-neu-theme/", + "category": "theme", + "description": { + "en": "Neumorphic DSH Web theme with light and dark palettes, ambient lighting, material shadows, grain texture, frosted-glass surfaces, and micro-interactions.", + "zh": "DSH Web 轻拟物主题:提供浅色与深色配色、环境光影、材质阴影、纹理、磨砂玻璃表面与微交互。" + }, + "npm": "dsh-neu-theme", + "stars": 6, + "downloads": 769, + "install": "dsh plugin --profile web add dsh-neu-theme", + "added": "2026-08-16" + }, + { + "name": "dsh-stock-terminal", + "owner": "linhut", + "url": "https://github.com/linhut/dsh-stock-terminal", + "page": "https://awesome-dsh-plugin.com/p/linhut/dsh-stock-terminal/", + "category": "theme", + "description": { + "en": "Stock market skin + real-time ticker plugin for DSH Web GUI: candlestick (K-line) popup with day/week/month periods, watchlist with pinyin fuzzy search, portfolio P&L tracker, trading session indicators, support for A-share/HK/US/crypto/forex.", + "zh": "股市行情皮肤与功能插件:全局交易终端皮肤 + 跑马灯 + 个股 K 线弹窗(日K/周K/月K 切换)、自选拼音首字母模糊搜索、持仓盈亏管理、交易时段指示,支持 A股 / 港股 / 美股 / 加密货币 / 外汇。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:linhut/dsh-stock-terminal", + "added": "2026-08-18" + }, + { + "name": "dsh-qq-skin", + "owner": "lispking", + "url": "https://github.com/lispking/dsh-qq-skin", + "page": "https://awesome-dsh-plugin.com/p/lispking/dsh-qq-skin/", + "category": "theme", + "description": { + "en": "A QQ NT messenger skin for DeepSeek Harness: light and dark share one QQ NT language — clean restrained light mode (brand blue #12B7F5, light-blue bubbles) and a calm blue-gray dark mode (#101822) — applied as reversible token and layout layers that never touch your Light/Dark preference.", + "zh": "为 DeepSeek Harness 打造的 QQ NT 皮肤:浅色与深色共用一套 QQ NT 语言——浅色干净克制(品牌蓝 #12B7F5、浅蓝气泡),深色为沉稳蓝灰(#101822)——以可逆的 token 与布局两层实现,不改变你的浅色/深色偏好。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:lispking/dsh-qq-skin", + "added": "2026-08-27" + }, + { + "name": "dsh-theme-firefly", + "owner": "Liu-ZA-81", + "url": "https://github.com/Liu-ZA-81/dsh-theme-firefly", + "page": "https://awesome-dsh-plugin.com/p/Liu-ZA-81/dsh-theme-firefly/", + "category": "theme", + "description": { + "en": "A Honkai: Star Rail \"Firefly\" theme with wallpaper/video backgrounds, firefly-green neon palette, transformation boot animation, ambient particles, BGM, typewriter SFX, and context-aware emote easter eggs.", + "zh": "崩坏:星穹铁道「流萤」主题:立绘/动态壁纸、萤火绿霓虹配色、开屏变身动画、萤火氛围粒子、背景音乐、打字音效与按对话触发的表情包彩蛋。" + }, + "npm": "dsh-theme-firefly", + "stars": 17, + "downloads": 732, + "install": "dsh plugin --profile web add dsh-theme-firefly", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/Liu-ZA-81/dsh-theme-firefly/main/docs/screenshots/01-wallpaper.jpg", + "https://raw.githubusercontent.com/Liu-ZA-81/dsh-theme-firefly/main/docs/screenshots/02-boot.jpg", + "https://raw.githubusercontent.com/Liu-ZA-81/dsh-theme-firefly/main/docs/screenshots/03-firefly.jpg", + "https://raw.githubusercontent.com/Liu-ZA-81/dsh-theme-firefly/main/docs/screenshots/04-music.jpg", + "https://raw.githubusercontent.com/Liu-ZA-81/dsh-theme-firefly/main/docs/screenshots/05-emote.jpg" + ] + }, + { + "name": "dsh-theme-customizer", + "owner": "lxxz1918", + "url": "https://github.com/lxxz1918/dsh-theme-customizer", + "page": "https://awesome-dsh-plugin.com/p/lxxz1918/dsh-theme-customizer/", + "category": "theme", + "description": { + "en": "Visual theme customizer for the DeepSeek Harness web UI — backgrounds, text colors, borders and details, all adjustable visually, with presets you can import and export, persisted across restarts.", + "zh": "DeepSeek Harness Web 界面自定义主题插件:背景/文字/框线/细节全可视化调整,可导入导出预设,持久化保存。" + }, + "npm": "dsh-theme-customizer", + "stars": 1, + "downloads": 758, + "install": "dsh plugin --profile web add dsh-theme-customizer", + "added": "2026-08-23", + "screenshots": [ + "https://raw.githubusercontent.com/lxxz1918/dsh-theme-customizer/HEAD/docs/screenshots/0-cover.jpg", + "https://raw.githubusercontent.com/lxxz1918/dsh-theme-customizer/HEAD/docs/screenshots/1-aurora/0-front-1.jpg", + "https://raw.githubusercontent.com/lxxz1918/dsh-theme-customizer/HEAD/docs/screenshots/2-sunset/0-front-1.jpg", + "https://raw.githubusercontent.com/lxxz1918/dsh-theme-customizer/HEAD/docs/screenshots/3-cyber/0-front-1.jpg", + "https://raw.githubusercontent.com/lxxz1918/dsh-theme-customizer/HEAD/docs/screenshots/4-sakura/0-front-1.jpg" + ] + }, + { + "name": "dsh-skin-nebula", + "owner": "lyq3", + "url": "https://github.com/lyq3/dsh-skin-nebula", + "page": "https://awesome-dsh-plugin.com/p/lyq3/dsh-skin-nebula/", + "category": "theme", + "description": { + "en": "Anime skin pack for the DSH Web UI: five AI-generated HD themes (Ninja, Sakura, Bamboo, Ronin, Ryujin) with per-theme accent palettes, a top-right shirt-icon switcher, and an adjustable transparency slider.", + "zh": "动漫皮肤包:忍者、樱花、竹林、落日、苍龙五套 AI 生成高清主题,各配专属点缀色,右上角衣服图标一键切换,透明度可调。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:lyq3/dsh-skin-nebula", + "added": "2026-08-18" + }, + { + "name": "dsh-themes", + "owner": "MangMax", + "url": "https://github.com/MangMax/dsh-themes", + "page": "https://awesome-dsh-plugin.com/p/MangMax/dsh-themes/", + "category": "theme", + "description": { + "en": "Look & theme plugin for the DSH Web UI: built-in palettes, light/dark/follow-system appearance modes, Open VSX theme search & install, and VS Code theme import with a persisted theme library.", + "zh": "DSH Web UI 外观与主题插件:内置调色板、明 / 暗 / 跟随系统外观模式、Open VSX 主题搜索导入与 VS Code 主题导入,主题库持久化。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:MangMax/dsh-themes", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/MangMax/dsh-themes/main/assets/screenshot.png" + ] + }, + { + "name": "dsh-skin-background", + "owner": "megatronyy", + "url": "https://github.com/megatronyy/dsh-skin-background", + "page": "https://awesome-dsh-plugin.com/p/megatronyy/dsh-skin-background/", + "category": "theme", + "description": { + "en": "Image-background skin with four shipped wallpapers, a user wallpaper directory, custom image URLs, and live dim/blur controls in Settings.", + "zh": "图片背景皮肤:内置四张壁纸,支持用户壁纸目录与自定义图片链接,设置页实时调节压暗与模糊。" + }, + "npm": null, + "tarball": "https://github.com/megatronyy/dsh-skin-background/releases/latest/download/dsh-skin-background-0.1.0.tgz", + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/megatronyy/dsh-skin-background/releases/latest/download/dsh-skin-background-0.1.0.tgz\"", + "added": "2026-08-23" + }, + { + "name": "dsh-darksoft-theme", + "owner": "MitterMeierGithub", + "url": "https://github.com/MitterMeierGithub/dsh-darksoft-theme", + "page": "https://awesome-dsh-plugin.com/p/MitterMeierGithub/dsh-darksoft-theme/", + "category": "theme", + "description": { + "en": "Dark theme plugin for dsh web (port of Obsidian things-soft-colorful-headings): warm-gray dimmed text via --dsw-alias-label-* token overrides plus soft colorful h1-h6 headings, zero dependencies.", + "zh": "dsh web 暗色主题插件(移植自 Obsidian things-soft-colorful-headings):通过 --dsw-alias-label-* token 覆盖实现暖灰调暗文本,配合柔和的彩色 h1-h6 标题,零依赖。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:MitterMeierGithub/dsh-darksoft-theme", + "added": "2026-08-24" + }, + { + "name": "dsh-ui-liteglass", + "owner": "mumuer1024", + "url": "https://github.com/mumuer1024/dsh-ui-liteglass", + "page": "https://awesome-dsh-plugin.com/p/mumuer1024/dsh-ui-liteglass/", + "category": "theme", + "description": { + "en": "A lightweight appearance skin for DeepSeek Harness: custom wallpaper, glass-like panel transparency, and an accent color; server-side settings shared across devices, native light/dark mode left untouched.", + "zh": "DeepSeek Harness 轻量级外观皮肤:自定义壁纸、玻璃质感面板与重点色;服务端持久化、多设备一致,不接管原生深浅色。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:mumuer1024/dsh-ui-liteglass", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/mumuer1024/dsh-ui-liteglass/HEAD/docs/screenshots/preview-light.webp", + "https://raw.githubusercontent.com/mumuer1024/dsh-ui-liteglass/HEAD/docs/screenshots/preview-dark.webp", + "https://raw.githubusercontent.com/mumuer1024/dsh-ui-liteglass/HEAD/docs/screenshots/settings-light.webp", + "https://raw.githubusercontent.com/mumuer1024/dsh-ui-liteglass/HEAD/docs/screenshots/settings-dark.webp" + ] + }, + { + "name": "dsh-theme", + "owner": "mux9056-bot", + "url": "https://github.com/mux9056-bot/dsh-theme", + "page": "https://awesome-dsh-plugin.com/p/mux9056-bot/dsh-theme/", + "category": "theme", + "description": { + "en": "Theme pack for the DSH Web UI: 30 drop-in CSS themes with light and dark variants, integrated into Settings → General with a programmatic API.", + "zh": "DSH Web UI 主题包:30 款即插即用的 CSS 主题,每款含浅色与深色变体,集成于「设置 → 通用」,并提供程序化 API。" + }, + "npm": "dsh-theme", + "stars": 4, + "downloads": 425, + "install": "dsh plugin --profile web add dsh-theme", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/mux9056-bot/dsh-theme/main/shots/preview-gallery.png", + "https://raw.githubusercontent.com/mux9056-bot/dsh-theme/main/shots/mono-dark.png", + "https://raw.githubusercontent.com/mux9056-bot/dsh-theme/main/shots/sakura-light.png", + "https://raw.githubusercontent.com/mux9056-bot/dsh-theme/main/shots/terminal-dark.png" + ] + }, + { + "name": "dsh-kimino-theme", + "owner": "niiang", + "url": "https://github.com/niiang/dsh-kimino-theme", + "page": "https://awesome-dsh-plugin.com/p/niiang/dsh-kimino-theme/", + "category": "theme", + "description": { + "en": "Kimi no Na wa (Your Name) theme: cinematic wallpaper, comet-blue glassmorphism, movie logo swap and unified scrollbars for the DSH Web GUI.", + "zh": "《你的名字。》主题:电影壁纸、彗星蓝玻璃拟态、电影 Logo 替换与统一滚动条。" + }, + "npm": null, + "stars": 24, + "downloads": null, + "install": "dsh plugin --profile web add github:niiang/dsh-kimino-theme", + "added": "2026-08-18" + }, + { + "name": "dsh-catppuccin-theme", + "owner": "NoNameLeGo", + "url": "https://github.com/NoNameLeGo/dsh-catppuccin-theme", + "page": "https://awesome-dsh-plugin.com/p/NoNameLeGo/dsh-catppuccin-theme/", + "category": "theme", + "description": { + "en": "Catppuccin themes plus a toggleable glassmorphism skin for the DSH Web GUI: Latte, Frappé, Macchiato and Mocha registered into the native theme runtime, one-click switching with a persisted choice, and adjustable frosted glass for the top bar, sidebar, composer, stats line and trajectory view.", + "zh": "DSH Web GUI 的 Catppuccin 主题插件:Latte、Frappé、Macchiato、Mocha 四套主题接入原生主题系统,一键切换并记住选择;另附可开关的玻璃质感皮肤,顶栏、侧边栏、输入框、统计行与轨迹视图的磨砂玻璃效果可自由调节。" + }, + "npm": "@nonamelego/dsh-catppuccin", + "stars": 20, + "downloads": 5806, + "install": "dsh plugin --profile web add @nonamelego/dsh-catppuccin", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/HEAD/assets/previews/glass-combined.png", + "https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/HEAD/assets/previews/combined.png", + "https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/HEAD/assets/previews/glass-latte.png", + "https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/HEAD/assets/previews/glass-mocha.png", + "https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/HEAD/assets/previews/latte.png", + "https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/HEAD/assets/previews/frappe.png", + "https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/HEAD/assets/previews/macchiato.png", + "https://raw.githubusercontent.com/NoNameLeGo/dsh-catppuccin-theme/HEAD/assets/previews/mocha.png" + ] + }, + { + "name": "dsh-dracula-theme", + "owner": "ossFrankFrank", + "url": "https://github.com/ossFrankFrank/dsh-dracula-theme", + "page": "https://awesome-dsh-plugin.com/p/ossFrankFrank/dsh-dracula-theme/", + "category": "theme", + "description": { + "en": "Dracula theme plugin for the DSH Web GUI: the classic dark palette and the Soft variant registered into the native theme runtime, one-click switching with a persisted choice.", + "zh": "DSH Web GUI 的德古拉主题插件:经典暗色配色与 Soft 变体接入原生主题系统,一键切换并记住选择。" + }, + "npm": "dsh-dracula-theme", + "stars": 2, + "downloads": 986, + "install": "dsh plugin --profile web add dsh-dracula-theme", + "added": "2026-08-18" + }, + { + "name": "dsh-client-ui-skin-claude", + "owner": "PAKIKNOWLEDGE", + "url": "https://github.com/PAKIKNOWLEDGE/dsh-client-ui-skin-claude", + "page": "https://awesome-dsh-plugin.com/p/PAKIKNOWLEDGE/dsh-client-ui-skin-claude/", + "category": "theme", + "description": { + "en": "Claude-style skin with a warm-black canvas, clay accent, and serif UI that follows the native light and dark themes.", + "zh": "Claude 风格皮肤:暖黑画布、陶橙点缀、衬线 UI,跟随原生亮/暗主题。" + }, + "npm": "@pakiknowledge/dsh-client-ui-skin-claude", + "stars": 9, + "downloads": 774, + "install": "dsh plugin --profile web add @pakiknowledge/dsh-client-ui-skin-claude", + "added": "2026-08-14" + }, + { + "name": "dsh-dream-skin", + "owner": "RevolutionLA", + "url": "https://github.com/RevolutionLA/dsh-dream-skin", + "page": "https://awesome-dsh-plugin.com/p/RevolutionLA/dsh-dream-skin/", + "category": "theme", + "description": { + "en": "One-command skin plugin: 8 original themes, translucent wallpaper with opacity/blur, per-user accent, and shareable theme-pack import/export, favorites and surprise-me — purely native on DSH's token system.", + "zh": "一键换肤插件:8 套原创主题、背景壁纸(透明度/模糊)、强调色、主题包导入/导出+分享链接、收藏与随机,纯原生 token 系统接入。" + }, + "npm": "dsh-dream-skin", + "stars": 111, + "downloads": 12475, + "install": "dsh plugin --profile web add dsh-dream-skin", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/RevolutionLA/dsh-dream-skin/main/docs/screenshots/preview.png", + "https://raw.githubusercontent.com/RevolutionLA/dsh-dream-skin/main/docs/screenshots/settings.png" + ] + }, + { + "name": "dsh-skin-digital-arcade", + "owner": "RizenHNT", + "url": "https://github.com/RizenHNT/dsh-skin-digital-arcade", + "page": "https://awesome-dsh-plugin.com/p/RizenHNT/dsh-skin-digital-arcade/", + "category": "theme", + "description": { + "en": "Digital arcade HUD skin: neon cyan/violet/magenta palette, Fusion Pixel fonts, animated sprites (city backdrop, data core, mascot), custom crosshair cursor; light and dark themes follow the system.", + "zh": "数码电玩风 HUD 皮肤:霓虹青/紫/品红配色、Fusion Pixel 像素字体、动画精灵(背景城市/数据核心/吉祥物)、自定义十字准星光标,浅深双主题跟随系统。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:RizenHNT/dsh-skin-digital-arcade", + "added": "2026-08-15" + }, + { + "name": "dsh-wallpaper-engine", + "owner": "ruijiaang-lab", + "url": "https://github.com/ruijiaang-lab/dsh-wallpaper-engine", + "page": "https://awesome-dsh-plugin.com/p/ruijiaang-lab/dsh-wallpaper-engine/", + "category": "theme", + "description": { + "en": "Wallpaper backgrounds for the DSH web GUI. Windows: reads your Wallpaper Engine (Steam) library and plays video/web wallpapers behind the chat. macOS: scans the WaifuX download folder and loose media folders automatically — anything saved in WaifuX becomes a DSH background with zero setup. Liquid-glass blur, dim and border sliders.", + "zh": "DSH Web 聊天背景壁纸。Windows:直接读取你的 Wallpaper Engine(Steam)库,把视频/网页壁纸渲染到聊天界面背后。macOS:自动扫描 WaifuX 下载目录和媒体文件夹——在 WaifuX 里保存的壁纸零配置即可成为 DSH 背景,支持视频与图片。带毛玻璃模糊、压暗、边框调节滑块。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:ruijiaang-lab/dsh-wallpaper-engine", + "added": "2026-08-18" + }, + { + "name": "dsh-wallpaper-engine", + "owner": "sakuraaa667", + "url": "https://github.com/sakuraaa667/dsh-wallpaper-engine", + "page": "https://awesome-dsh-plugin.com/p/sakuraaa667/dsh-wallpaper-engine/", + "category": "theme", + "description": { + "en": "Use wallpapers downloaded in Wallpaper Engine as the DeepSeek Harness web background: video wallpapers play in place, image wallpapers use the highest-resolution standalone artwork, with cover/contain fit modes and a 0-100 panel dimmer.", + "zh": "将 Wallpaper Engine 中已下载的壁纸用作 DeepSeek Harness 的背景:视频壁纸原位播放,图片壁纸优先使用最高分辨率原图,支持铺满/完整显示适配与 0–100 面板暗化。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:sakuraaa667/dsh-wallpaper-engine", + "added": "2026-08-18" + }, + { + "name": "dsh-client-ui-theme-xp", + "owner": "SamizuHM", + "url": "https://github.com/SamizuHM/dsh-client-ui-theme-xp", + "page": "https://awesome-dsh-plugin.com/p/SamizuHM/dsh-client-ui-theme-xp/", + "category": "theme", + "description": { + "en": "Windows XP Luna desktop for the DSH Web UI: a floating window manager with taskbar and desktop icons, plus the era-accurate Luna skin.", + "zh": "Windows XP Luna 桌面化主题:浮动窗口管理器(任务栏、桌面图标)加上还原度很高的 Luna 皮肤。" + }, + "npm": "dsh-client-ui-theme-xp", + "stars": 7, + "downloads": 506, + "install": "dsh plugin --profile web add dsh-client-ui-theme-xp", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/SamizuHM/dsh-client-ui-theme-xp/main/docs/screenshot.png" + ] + }, + { + "name": "dsh-client-ui-skin-verdandi", + "owner": "Sddft97", + "url": "https://github.com/Sddft97/dsh-client-ui-skin-verdandi", + "page": "https://awesome-dsh-plugin.com/p/Sddft97/dsh-client-ui-skin-verdandi/", + "category": "theme", + "description": { + "en": "An Aether Gazer Verdandi-inspired DSH Web UI skin with light and dark backgrounds, wedding-themed chrome, responsive character art, and themed conversation and sidebar surfaces.", + "zh": "一款以《深空之眼》薇儿丹蒂为主题的 DSH Web UI 皮肤,包含昼夜背景、誓约婚礼装饰、响应式角色立绘,以及主题化的会话区与侧边栏。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Sddft97/dsh-client-ui-skin-verdandi", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/Sddft97/dsh-client-ui-skin-verdandi/HEAD/preview/light.png", + "https://raw.githubusercontent.com/Sddft97/dsh-client-ui-skin-verdandi/HEAD/preview/dark.png", + "https://raw.githubusercontent.com/Sddft97/dsh-client-ui-skin-verdandi/HEAD/preview/settings.png" + ] + }, + { + "name": "dsh-frosted-window", + "owner": "SenryLee", + "url": "https://github.com/SenryLee/dsh-frosted-window", + "page": "https://awesome-dsh-plugin.com/p/SenryLee/dsh-frosted-window/", + "category": "theme", + "description": { + "en": "Upload an image as a full-window frosted-glass theme: sidebar/conversation/details share one frost layer, Save/Delete, official Light/Dark/System stay in charge.", + "zh": "上传图片作为整窗磨砂玻璃主题:侧栏/会话/详情同一层磨砂,保存/删除,官方浅色/深色/跟随系统仍有效。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:SenryLee/dsh-frosted-window", + "added": "2026-08-16" + }, + { + "name": "dsh-theme-tuner", + "owner": "shawnlone", + "url": "https://github.com/shawnlone/dsh-theme-tuner", + "page": "https://awesome-dsh-plugin.com/p/shawnlone/dsh-theme-tuner/", + "category": "theme", + "description": { + "en": "Add a theme-adjustment panel under Appearance to customize accent, background, foreground, contrast and gradient, applied live through theme token overrides.", + "zh": "在「外观」下增加一个主题调节面板,可自定义强调色/背景色/前景色/对比度/渐变度,并实时生效。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:shawnlone/dsh-theme-tuner", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/shawnlone/dsh-theme-tuner/HEAD/assets/screenshot_01.png", + "https://raw.githubusercontent.com/shawnlone/dsh-theme-tuner/HEAD/assets/screenshot_02.png", + "https://raw.githubusercontent.com/shawnlone/dsh-theme-tuner/HEAD/assets/screenshot_03.png", + "https://raw.githubusercontent.com/shawnlone/dsh-theme-tuner/HEAD/assets/screenshot_04.png", + "https://raw.githubusercontent.com/shawnlone/dsh-theme-tuner/HEAD/assets/screenshot_05.png", + "https://raw.githubusercontent.com/shawnlone/dsh-theme-tuner/HEAD/assets/screenshot_06.png" + ] + }, + { + "name": "dsh-vscode-theme", + "owner": "Sim-xia", + "url": "https://github.com/Sim-xia/dsh-vscode-theme", + "page": "https://awesome-dsh-plugin.com/p/Sim-xia/dsh-vscode-theme/", + "category": "theme", + "description": { + "en": "Imports .vsix theme files made for Visual Studio Code and applies them to the DSH Web UI.", + "zh": "导入为 Visual Studio Code 设计的 .vsix 主题文件并应用到 DSH Web UI。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Sim-xia/dsh-vscode-theme", + "added": "2026-08-15" + }, + { + "name": "dsh-theme-escook", + "owner": "Simon-yyy", + "url": "https://github.com/Simon-yyy/dsh-theme-escook", + "page": "https://awesome-dsh-plugin.com/p/Simon-yyy/dsh-theme-escook/", + "category": "theme", + "description": { + "en": "Signature escook theme ported from VS Code: 4 classic aesthetic color schemes (Dark, Dark Soft, Light, Light Soft) with high-contrast syntax highlighting for DeepSeek Harness.", + "zh": "移植自 VS Code 经典的彬哥主题:包含 4 款经典美学配色(经典暗黑、柔和暗黑、紫韵浅色、柔和浅色)与高对比度语法高亮。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Simon-yyy/dsh-theme-escook", + "added": "2026-08-28" + }, + { + "name": "dsh-deep-diving", + "owner": "skymecode", + "url": "https://github.com/skymecode/dsh-deep-diving", + "page": "https://awesome-dsh-plugin.com/p/skymecode/dsh-deep-diving/", + "category": "theme", + "description": { + "en": "Adds selectable animated skins to the \"Deep diving...\" status row, with configurable size and optional status-text replacement.", + "zh": "为“Deep diving...”状态行添加可选动态皮肤,并支持调整尺寸和可选状态文案替换。" + }, + "npm": null, + "tarball": "https://github.com/skymecode/dsh-deep-diving/releases/download/v0.1.0/dsh-deep-dive-skins-0.1.0.tgz", + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/skymecode/dsh-deep-diving/releases/download/v0.1.0/dsh-deep-dive-skins-0.1.0.tgz\"", + "added": "2026-08-23" + }, + { + "name": "dsh-custom-skin", + "owner": "SLin-code", + "url": "https://github.com/SLin-code/dsh-custom-skin", + "page": "https://awesome-dsh-plugin.com/p/SLin-code/dsh-custom-skin/", + "category": "theme", + "description": { + "en": "Browser-local wallpaper and translucent-panel controls for DSH Web, with multiple images, fit and position choices, shading, blur, and panel opacity.", + "zh": "DSH Web 的浏览器本地壁纸与半透明面板插件,支持多图切换、填充与位置选择、遮罩、模糊和面板透明度调节。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:SLin-code/dsh-custom-skin", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/SLin-code/dsh-custom-skin/HEAD/docs/images/wallpaper-blue.webp", + "https://raw.githubusercontent.com/SLin-code/dsh-custom-skin/HEAD/docs/images/personalization-settings.webp", + "https://raw.githubusercontent.com/SLin-code/dsh-custom-skin/HEAD/docs/images/wallpaper-pink.webp" + ] + }, + { + "name": "dsh-deep-whale#maid-atelier", + "owner": "Small-tailqwq", + "url": "https://github.com/Small-tailqwq/dsh-deep-whale/tree/main/maid-atelier", + "page": "https://awesome-dsh-plugin.com/p/Small-tailqwq/dsh-deep-whale--maid-atelier/", + "category": "theme", + "description": { + "en": "Whale-girl skin series for the DSH Web UI (maid-atelier).", + "zh": "DSH Web 鲸鱼娘皮肤系列(深海女仆工坊 maid-atelier)。" + }, + "npm": null, + "stars": 1717, + "downloads": null, + "install": "dsh plugin --profile web add github:Small-tailqwq/dsh-deep-whale#path:/maid-atelier", + "added": "2026-08-16" + }, + { + "name": "harness-plugin", + "owner": "Smith-yue", + "url": "https://github.com/Smith-yue/harness-plugin", + "page": "https://awesome-dsh-plugin.com/p/Smith-yue/harness-plugin/", + "category": "theme", + "description": { + "en": "Adds two switchable themes, built-in and local-folder background slideshows, and per-session token usage details to DeepSeek Harness Web and Desktop.", + "zh": "为 DeepSeek Harness Web 与桌面端提供两套可切换主题、内置及本地文件夹背景轮播和逐会话 Token 用量明细。" + }, + "npm": "dsh-skin-token-dashboard", + "stars": 1, + "downloads": 171, + "install": "dsh plugin --profile web add dsh-skin-token-dashboard", + "added": "2026-08-24" + }, + { + "name": "dsh-blue-whale", + "owner": "starslittle", + "url": "https://github.com/starslittle/dsh-blue-whale", + "page": "https://awesome-dsh-plugin.com/p/starslittle/dsh-blue-whale/", + "category": "theme", + "description": { + "en": "A DeepSeek Chat-style blue-whale color skin. Light and dark follow the built-in appearance.", + "zh": "复刻 DeepSeek Chat 蓝鲸配色的皮肤,亮色/深色跟随系统外观。" + }, + "npm": "dsh-blue-whale", + "stars": 7, + "downloads": 272, + "install": "dsh plugin --profile web add dsh-blue-whale", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/starslittle/dsh-blue-whale/main/docs/compare-home.png", + "https://raw.githubusercontent.com/starslittle/dsh-blue-whale/main/docs/compare-brand.png" + ] + }, + { + "name": "beautiCode#deepseek-harness", + "owner": "starsstreaming", + "url": "https://github.com/starsstreaming/beautiCode/tree/main/integrations/deepseek-harness", + "page": "https://awesome-dsh-plugin.com/p/starsstreaming/beautiCode--integrations-deepseek-harness/", + "category": "theme", + "description": { + "en": "Local image and MP4 video backgrounds for DSH Web: a sidebar Background panel, saved themes including a built-in window-frame preset, mute toggle, last-background restore, plus /bg, /bg-theme and /bg-clear commands.", + "zh": "为 DSH Web 设置本地图片与 MP4 视频背景:侧栏「背景」面板、可保存主题(含内置「画窗」)、声音开关、恢复上次背景,以及 /bg、/bg-theme、/bg-clear 命令。" + }, + "npm": "beauticode-dsh", + "stars": 67, + "downloads": 2806, + "install": "dsh plugin --profile web add beauticode-dsh", + "added": "2026-08-19", + "screenshots": [ + "https://github.com/user-attachments/assets/c943a0fb-ff48-4361-9e6f-c4b1521aee2b", + "https://github.com/user-attachments/assets/a9a18412-4c62-4083-ab49-d127f05e61c3" + ] + }, + { + "name": "claude-style-skin", + "owner": "TaiyakiOffical", + "url": "https://github.com/TaiyakiOffical/claude-style-skin", + "page": "https://awesome-dsh-plugin.com/p/TaiyakiOffical/claude-style-skin/", + "category": "theme", + "description": { + "en": "Claude-style warm-editorial skin for the DSH web GUI — ivory/warm-black canvas, a clay ember accent, serif display + sans UI + mono labels, hairline borders and pill CTAs, light/dark themes.", + "zh": "Claude 风格暖调编辑 DSH Web 皮肤:象牙白/暖黑双画布、陶烬橙点睛、衬线标题+无衬线界面+等宽标签、发丝线与胶囊 CTA,亮暗双主题跟随系统。" + }, + "npm": "claude-style-skin", + "stars": 0, + "downloads": 292, + "install": "dsh plugin --profile web add claude-style-skin", + "added": "2026-08-25" + }, + { + "name": "dsh-minecraft-ui", + "owner": "TFboy1", + "url": "https://github.com/TFboy1/dsh-minecraft-ui", + "page": "https://awesome-dsh-plugin.com/p/TFboy1/dsh-minecraft-ui/", + "category": "theme", + "description": { + "en": "Presents DSH Web as a playable, full-screen first-person Minecraft-style voxel interface—not a color-only skin: users walk through a persistent Three.js block world and access native workspaces, sessions, conversations, tools, model selection, permissions, context information, and the composer through in-world facilities and a workbench.", + "zh": "将 DSH Web 直接呈现为可游玩的全屏第一人称 Minecraft 风格体素界面,而非只更换颜色的皮肤:用户可以在可持久化的 Three.js 方块世界中移动,并通过游戏内设施和工作台使用原生工作区、会话、对话、工具、模型选择、权限、上下文信息与输入框。" + }, + "npm": "dsh-minecraft-ui", + "stars": 5, + "downloads": 224, + "install": "dsh plugin --profile web add dsh-minecraft-ui", + "added": "2026-08-21" + }, + { + "name": "dsh-skin-toggle", + "owner": "tiantyu", + "url": "https://github.com/tiantyu/dsh-skin-toggle", + "page": "https://awesome-dsh-plugin.com/p/tiantyu/dsh-skin-toggle/", + "category": "theme", + "description": { + "en": "Skin manager for the DeepSeek Harness web GUI: a draggable whale button that restores the default look on left-click and switches between installed skins on right-click.", + "zh": "DSH 网页皮肤管理器:可拖动的鲸鱼按钮,左键恢复默认界面,右键列出并切换所有已安装皮肤。" + }, + "npm": null, + "tarball": "https://github.com/tiantyu/dsh-skin-toggle/releases/latest/download/dsh-skin-toggle.tgz", + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/tiantyu/dsh-skin-toggle/releases/latest/download/dsh-skin-toggle.tgz\"", + "added": "2026-08-17" + }, + { + "name": "dsh-font", + "owner": "tianyhjg-lab", + "url": "https://github.com/tianyhjg-lab/dsh-font", + "page": "https://awesome-dsh-plugin.com/p/tianyhjg-lab/dsh-font/", + "category": "theme", + "description": { + "en": "Font switcher for the DSH Web GUI: 99 UI fonts and 31 code fonts with CJK-Latin pairing stacks, instant apply and localStorage persistence.", + "zh": "DSH Web GUI 字体切换器:99 个界面字体与 31 个代码字体,中西文自动搭配,即选即生效,localStorage 持久化。" + }, + "npm": null, + "stars": 12, + "downloads": null, + "install": "dsh plugin --profile web add github:tianyhjg-lab/dsh-font", + "added": "2026-08-14" + }, + { + "name": "dsh-any-background", + "owner": "Tkingxiao", + "url": "https://github.com/Tkingxiao/dsh-any-background", + "page": "https://awesome-dsh-plugin.com/p/Tkingxiao/dsh-any-background/", + "category": "theme", + "description": { + "en": "Custom theme plugin with video and image wallpapers, free positioning, and scale/fill modes. Extracts wallpaper colours and switches text to light or dark automatically. Per-area blur across 6 UI regions, per-area opacity, and one-click theme export/import.", + "zh": "自定义主题插件:支持视频与图片壁纸、自由拖动位置与拉伸填充模式;从壁纸提取主色并自动切换明暗字体;六个界面区域的模糊分别可调,分区不透明度可调;主题一键导出与导入。" + }, + "npm": "dsh-any-background", + "stars": 17, + "downloads": 3030, + "install": "dsh plugin --profile web add dsh-any-background", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/Tkingxiao/dsh-any-background/main/example_img/image.png", + "https://raw.githubusercontent.com/Tkingxiao/dsh-any-background/main/example_img/image-2.png", + "https://raw.githubusercontent.com/Tkingxiao/dsh-any-background/main/example_img/image-3.png", + "https://raw.githubusercontent.com/Tkingxiao/dsh-any-background/main/example_img/image-4.png", + "https://raw.githubusercontent.com/Tkingxiao/dsh-any-background/main/example_img/image-6.png", + "https://raw.githubusercontent.com/Tkingxiao/dsh-any-background/main/example_img/image-9.png", + "https://raw.githubusercontent.com/Tkingxiao/dsh-any-background/main/example_img/image-10.png" + ] + }, + { + "name": "dsh-theme-cyberpunk2077", + "owner": "Tommy00748", + "url": "https://github.com/Tommy00748/dsh-theme-cyberpunk2077", + "page": "https://awesome-dsh-plugin.com/p/Tommy00748/dsh-theme-cyberpunk2077/", + "category": "theme", + "description": { + "en": "Cyberpunk 2077 / Night City theme: NC yellow and neon cyan identity, CRT scanlines, Kiroshi hover lock-on, combat-state HUD, synthesized typewriter and message SFX, and hidden easter eggs (relic / johnny).", + "zh": "Cyberpunk 2077 / 夜之城主题:NC 黄 × 霓虹青配色、CRT 扫描线、Kiroshi 悬停锁定、战斗状态 HUD、合成打字机与消息音效,以及隐藏彩蛋(relic / johnny)。" + }, + "npm": null, + "stars": 25, + "downloads": null, + "install": "dsh plugin --profile web add github:Tommy00748/dsh-theme-cyberpunk2077", + "added": "2026-08-15" + }, + { + "name": "dsh-joi-channel-theme", + "owner": "tpmoonchefryan", + "url": "https://github.com/tpmoonchefryan/dsh-joi-channel-theme", + "page": "https://awesome-dsh-plugin.com/p/tpmoonchefryan/dsh-joi-channel-theme/", + "category": "theme", + "description": { + "en": "Two-suit VTuber theme for Joi: Flowers and Library are separate token systems for light and dark, with a standing portrait and a whale girl napping on the headline of the new-session page, two chibi companions above the composer that follow the turn state, a duo sidebar mark, and a context meter shaped as a ripening mandarin; a third card returns the stock DeepSeek look with the plugin still installed.", + "zh": "轴伊 Joi 双衣装主题:Flowers 与 Library 是各自成套的明暗令牌体系,新会话页有立绘与趴在标题上的鲸鱼娘,输入框上方两只 Q 版角色随本回合状态换表情,侧栏字标换成二人合影,上下文占用由一颗渐熟的橘子表示;第三张卡可在保持插件安装的前提下切回 DeepSeek 原生外观。" + }, + "npm": "dsh-joi-channel-theme", + "stars": 12, + "downloads": 1636, + "install": "dsh plugin --profile web add dsh-joi-channel-theme", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/tpmoonchefryan/dsh-joi-channel-theme/main/docs/settings-wardrobe.png", + "https://raw.githubusercontent.com/tpmoonchefryan/dsh-joi-channel-theme/main/docs/settings-wardrobe-dark.png", + "https://raw.githubusercontent.com/tpmoonchefryan/dsh-joi-channel-theme/main/docs/new-chat-flowers.png", + "https://raw.githubusercontent.com/tpmoonchefryan/dsh-joi-channel-theme/main/docs/new-chat-library.png", + "https://raw.githubusercontent.com/tpmoonchefryan/dsh-joi-channel-theme/main/docs/new-chat-flowers-dark.png", + "https://raw.githubusercontent.com/tpmoonchefryan/dsh-joi-channel-theme/main/docs/working-flowers.png", + "https://raw.githubusercontent.com/tpmoonchefryan/dsh-joi-channel-theme/main/docs/success-library.png", + "https://raw.githubusercontent.com/tpmoonchefryan/dsh-joi-channel-theme/main/docs/app-logo-flowers.png" + ] + }, + { + "name": "dsh-ui-appearance", + "owner": "TQSY114514", + "url": "https://github.com/TQSY114514/dsh-ui-appearance", + "page": "https://awesome-dsh-plugin.com/p/TQSY114514/dsh-ui-appearance/", + "category": "theme", + "description": { + "en": "Appearance customization for the DeepSeek Harness WebUI: theme palette, wallpaper and video backgrounds, translucency and glassmorphism.", + "zh": "DeepSeek Harness 外观自定义:主题调色、壁纸与视频背景、半透明与毛玻璃。" + }, + "npm": "dsh-ui-appearance", + "stars": 10, + "downloads": 2222, + "install": "dsh plugin --profile web add dsh-ui-appearance", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/TQSY114514/dsh-ui-appearance/HEAD/docs/screenshot-settings.png", + "https://raw.githubusercontent.com/TQSY114514/dsh-ui-appearance/HEAD/docs/screenshot-wallpaper.png" + ] + }, + { + "name": "dsh-skin-switcher", + "owner": "tsdfy", + "url": "https://github.com/tsdfy/dsh-skin-switcher", + "page": "https://awesome-dsh-plugin.com/p/tsdfy/dsh-skin-switcher/", + "category": "theme", + "description": { + "en": "Skin switcher for the dsh web UI: one-click theme switching with auto-discovery of community skins.", + "zh": "皮肤切换器:右上角一键切换主题,自动发现社区皮肤。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:tsdfy/dsh-skin-switcher", + "added": "2026-08-23" + }, + { + "name": "dsh-whale-background", + "owner": "tuogusa", + "url": "https://github.com/tuogusa/dsh-whale-background", + "page": "https://awesome-dsh-plugin.com/p/tuogusa/dsh-whale-background/", + "category": "theme", + "description": { + "en": "Whale-girl wallpaper background with a frosted semi-transparent app surface; customize image, opacity, and contain/cover from the Web settings, with remote image preflight and no-referrer loading.", + "zh": "Web UI 鲸鱼娘壁纸背景,应用表面半透明磨砂;支持在设置页自定义图片、透明度与 contain/cover 铺放方式,远程图片自动预检并 no-referrer 加载。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:tuogusa/dsh-whale-background", + "added": "2026-08-15" + }, + { + "name": "dsh-liquid-glass", + "owner": "Ultronen", + "url": "https://github.com/Ultronen/dsh-liquid-glass", + "page": "https://awesome-dsh-plugin.com/p/Ultronen/dsh-liquid-glass/", + "category": "theme", + "description": { + "en": "One toggle makes the whole DeepSeek Harness shell translucent, with an opacity slider and a custom full-page background.", + "zh": "一键让整个 DeepSeek Harness 界面通透起来:透明度滑块随心调,背景图自由换。" + }, + "npm": "dsh-liquid-glass", + "stars": 4, + "downloads": 676, + "install": "dsh plugin --profile web add dsh-liquid-glass", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/Ultronen/dsh-liquid-glass/main/assets/screenshots/1-settings.png", + "https://raw.githubusercontent.com/Ultronen/dsh-liquid-glass/main/assets/screenshots/2-glass-shell.png", + "https://raw.githubusercontent.com/Ultronen/dsh-liquid-glass/main/assets/screenshots/3-settings-wallpaper.png" + ] + }, + { + "name": "dsh-bloom-theme", + "owner": "webkubor", + "url": "https://github.com/webkubor/dsh-bloom-theme", + "page": "https://awesome-dsh-plugin.com/p/webkubor/dsh-bloom-theme/", + "category": "theme", + "description": { + "en": "Bloom Morandi theme: four variants (mist, cinnabar, petal, ripple) built on OKLCH so light and dark stay perceptually even, switchable from the top bar, with all eight foreground/background pairs measured at WCAG AA or better.", + "zh": "Bloom 莫兰迪主题:四款变体(雾蓝 mist、朱砂 cinnabar、花瓣 petal、涟漪 ripple)基于 OKLCH 调色,明暗切换不跳变,顶栏一键切换,八组「主色 + 底色」实测全部达到 WCAG AA。" + }, + "npm": "@kubor/dsh-bloom-theme", + "stars": 13, + "downloads": 2758, + "install": "dsh plugin --profile web add @kubor/dsh-bloom-theme", + "added": "2026-08-19" + }, + { + "name": "dsh-dynamic-wallpaper", + "owner": "Willmylife", + "url": "https://github.com/Willmylife/dsh-dynamic-wallpaper", + "page": "https://awesome-dsh-plugin.com/p/Willmylife/dsh-dynamic-wallpaper/", + "category": "theme", + "description": { + "en": "Animated wallpaper plugin with 8 canvas backgrounds (particles, meteors, starfield, waves, rain, bubbles, matrix, aurora) plus custom video import, and speed/density/opacity/blur sliders with localStorage persistence.", + "zh": "动态壁纸插件:8 种内置 Canvas 壁纸(粒子、流星雨、星际穿梭、波光、雨幕、气泡、字符雨、极光)加自定义视频背景,支持速度/密度/遮罩/模糊调节,配置持久化。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Willmylife/dsh-dynamic-wallpaper", + "added": "2026-08-18" + }, + { + "name": "dsh-plugins#dsh-appearance-gallery", + "owner": "wsxwj123", + "url": "https://github.com/wsxwj123/dsh-plugins/tree/main/packages/dsh-appearance-gallery", + "page": "https://awesome-dsh-plugin.com/p/wsxwj123/dsh-plugins--packages-dsh-appearance-gallery/", + "category": "theme", + "description": { + "en": "Fifteen curated theme families and nine complete dsh-web-ui skin replicas in one plugin behind a single settings entry, with CSS-only custom theme JSON, controlled custom skin-pack import via folder picker, preview, apply, delete and restore-default.", + "zh": "15 个精选主题家族 + 9 个完整 dsh-web-ui 皮肤复刻合并为单一插件、设置页单一入口,支持 CSS-only 自定义主题 JSON、受控自定义皮肤包导入(可直接选文件夹)、试穿、应用、删除与恢复默认。" + }, + "npm": "dsh-appearance-gallery", + "stars": 8, + "downloads": 296, + "install": "dsh plugin --profile web add dsh-appearance-gallery", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/wsxwj123/dsh-plugins/main/assets/screenshots/appearance-gallery.png" + ] + }, + { + "name": "dsh-api-key-pool", + "owner": "xiaozhe7772222", + "url": "https://github.com/xiaozhe7772222/dsh-api-key-pool", + "page": "https://awesome-dsh-plugin.com/p/xiaozhe7772222/dsh-api-key-pool/", + "category": "theme", + "description": { + "en": "API key rotation pool for your LLM providers: auto-discovers providers from settings, per-provider multi-key round-robin, failover on 401/403/429, and cooldown recovery.", + "zh": "对话模型 API Key 轮换池:自动检测 settings 中的厂商,每个厂商下多 Key 轮询,401/403/429 自动切换,冷却恢复。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:xiaozhe7772222/dsh-api-key-pool", + "added": "2026-08-15" + }, + { + "name": "dsh-liquid-glass", + "owner": "xingyingyuzhui", + "url": "https://github.com/xingyingyuzhui/dsh-liquid-glass", + "page": "https://awesome-dsh-plugin.com/p/xingyingyuzhui/dsh-liquid-glass/", + "category": "theme", + "description": { + "en": "Wallpaper plus optional Liquid Glass islands for the DSH Web UI: Ice/Deepwater presets, import, wallpaper opacity, and per-island blur on official light/dark/system.", + "zh": "DSH Web 液态玻璃皮肤:冰原/深水壁纸、可导入、壁纸透明度,以及叠在官方浅色/深色/跟随系统上的分岛模糊。" + }, + "npm": null, + "stars": 12, + "downloads": null, + "install": "dsh plugin --profile web add github:xingyingyuzhui/dsh-liquid-glass", + "added": "2026-08-15" + }, + { + "name": "dsh-theme-endfield", + "owner": "ymh0000123", + "url": "https://github.com/ymh0000123/dsh-theme-endfield", + "page": "https://awesome-dsh-plugin.com/p/ymh0000123/dsh-theme-endfield/", + "category": "theme", + "description": { + "en": "Arknights: Endfield industrial-editorial theme for the DSH Web UI: cream paper, ink text, zero border radius, two switchable accent palettes (signal yellow / teal-cyan), plus optional marching-squares contour background, ENDFIELD watermark and boot progress plate.", + "zh": "《明日方舟:终末地》工业编辑风主题:奶油纸底、墨黑文字、全局直角,两套强调色配色(谷地黄信号黄 / 武陵青青碧色)可在设置切换,另有可选的等高线地形背景、ENDFIELD 水印与启动加载屏。" + }, + "npm": null, + "stars": 39, + "downloads": null, + "install": "dsh plugin --profile web add github:ymh0000123/dsh-theme-endfield", + "added": "2026-08-23" + }, + { + "name": "dsh-maid-whale-webUI#maid-whale-webui", + "owner": "yunxiiQwQ", + "url": "https://github.com/yunxiiQwQ/dsh-maid-whale-webUI/tree/main/maid-whale-webui", + "page": "https://awesome-dsh-plugin.com/p/yunxiiQwQ/dsh-maid-whale-webUI--maid-whale-webui/", + "category": "theme", + "description": { + "en": "DSH Web UI little whale-maid theme with light and dark palettes, ocean illustrations, hand-drawn frames, decorative assets, and a new built-in Codex-style pet.", + "zh": "DSH Web UI 小鲸鱼女仆主题:亮暗配色、海洋插画、手绘边框、装饰素材与新增内置codex同款pet。" + }, + "npm": null, + "stars": 25, + "downloads": null, + "install": "dsh plugin --profile web add github:yunxiiQwQ/dsh-maid-whale-webUI#path:/maid-whale-webui", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/yunxiiQwQ/dsh-maid-whale-webUI/main/maid-whale-webui/preview/light.webp", + "https://raw.githubusercontent.com/yunxiiQwQ/dsh-maid-whale-webUI/main/maid-whale-webui/preview/dark.webp" + ] + }, + { + "name": "dsh-bloub-mood", + "owner": "Yuuhann1999", + "url": "https://github.com/Yuuhann1999/dsh-bloub-mood", + "page": "https://awesome-dsh-plugin.com/p/Yuuhann1999/dsh-bloub-mood/", + "category": "theme", + "description": { + "en": "Animated mood icon: swaps the favicon, sidebar wordmark and hero headline for bloub expression animations that follow session state (working / awaiting input / done / idle), with 8 shapes, 12 colors and custom wordmark texts configurable in Settings.", + "zh": "动态心情图标:favicon、侧栏字标与首页标题随会话状态切换 bloub 表情动画(执行中 / 等输入 / 完成 / 空闲),8 种形状、12 种颜色与自定义文字均可在设置页配置。" + }, + "npm": "dsh-bloub-mood", + "stars": 4, + "downloads": 2615, + "install": "dsh plugin --profile web add dsh-bloub-mood", + "added": "2026-08-19" + }, + { + "name": "dsh-icon-theme", + "owner": "yzke", + "url": "https://github.com/yzke/dsh-icon-theme", + "page": "https://awesome-dsh-plugin.com/p/yzke/dsh-icon-theme/", + "category": "theme", + "description": { + "en": "Automatic and user-customizable Fluent-style icons for DSH Settings and sidebar actions, with offline SVG packs and original-icon preservation.", + "zh": "为 DSH 设置和侧边栏自动分配并支持自定义的 Fluent 风格图标,离线内置 SVG,默认保留插件原图标。" + }, + "npm": "dsh-icon-theme", + "stars": 3, + "downloads": 1009, + "install": "dsh plugin --profile web add dsh-icon-theme", + "added": "2026-08-18" + }, + { + "name": "dsh-diorama", + "owner": "ZaVang", + "url": "https://github.com/ZaVang/dsh-diorama", + "page": "https://awesome-dsh-plugin.com/p/ZaVang/dsh-diorama/", + "category": "theme", + "description": { + "en": "Character skins with an editable decoration board — Yukino warm daily and occult editorial themes, each with dual character sprites + sticker sheets, drag/resize/rotate coordinate editor, custom asset upload, and shareable diorama packs (assets + layout bundled).", + "zh": "角色皮肤舞台:雪乃·暖阳日常与隐秘年代志双主题,各带双角色立绘与表情贴纸;可视化装饰看板支持拖动/缩放/旋转/坐标精调/锚定切换/上传素材,装饰包(素材+布局)可导出分享、导入后自动打开看板继续调整。" + }, + "npm": "dsh-diorama", + "stars": 1, + "downloads": 200, + "install": "dsh plugin --profile web add dsh-diorama", + "added": "2026-08-18" + }, + { + "name": "Catppuccin-dsh-theme", + "owner": "zhijun-dai", + "url": "https://github.com/zhijun-dai/Catppuccin-dsh-theme", + "page": "https://awesome-dsh-plugin.com/p/zhijun-dai/Catppuccin-dsh-theme/", + "category": "theme", + "description": { + "en": "Catppuccin theme plugin: Latte, Frappé, Macchiato, and Mocha skins for the DSH Web theme runtime.", + "zh": "Catppuccin 主题插件:为 DSH Web 主题运行时提供 Latte、Frappé、Macchiato、Mocha 四套皮肤。" + }, + "npm": "dsh-catppuccin", + "stars": 11, + "downloads": 1096, + "install": "dsh plugin --profile web add dsh-catppuccin", + "added": "2026-08-15" + }, + { + "name": "dsh-Fonts", + "owner": "zhijun-dai", + "url": "https://github.com/zhijun-dai/dsh-Fonts", + "page": "https://awesome-dsh-plugin.com/p/zhijun-dai/dsh-Fonts/", + "category": "theme", + "description": { + "en": "Font system plugin: OFL webfont presets served offline from the plugin, user-imported custom woff2 fonts, and a ctx.fonts registry other plugins can extend.", + "zh": "字体系统插件:随插件离线分发的 OFL 开源字体预设、自定义 woff2 字体导入,以及供其他插件扩展的 ctx.fonts 注册表。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:zhijun-dai/dsh-Fonts", + "added": "2026-08-15" + }, + { + "name": "Solarized-dsh-theme", + "owner": "zhijun-dai", + "url": "https://github.com/zhijun-dai/Solarized-dsh-theme", + "page": "https://awesome-dsh-plugin.com/p/zhijun-dai/Solarized-dsh-theme/", + "category": "theme", + "description": { + "en": "Solarized and Selenized theme plugin: four faithful palettes registered with the DSH Web theme runtime.", + "zh": "Solarized 与 Selenized 主题插件:向 DSH Web 主题运行时注册四套忠实色板。" + }, + "npm": "@yuquexianzhou/solarized-dsh-theme", + "stars": 6, + "downloads": 357, + "install": "dsh plugin --profile web add @yuquexianzhou/solarized-dsh-theme", + "added": "2026-08-15" + }, + { + "name": "dsh-theme-whalegirl", + "owner": "ZHOUcourier", + "url": "https://github.com/ZHOUcourier/dsh-theme-whalegirl", + "page": "https://awesome-dsh-plugin.com/p/ZHOUcourier/dsh-theme-whalegirl/", + "category": "theme", + "description": { + "en": "Whale Girl light theme for the DSH Web UI, ported from a DreamSkin skin package: registers one native runtime theme with a full --dsw-* token remap derived from the source palette, layers the artwork as an ambient fixed wallpaper behind a frosted UI, and pins the theme against appearance-preference resets.", + "zh": "移植自 DreamSkin 皮肤包的鲸鱼娘亮色主题:在原生主题运行时注册一套完整 --dsw-* 令牌重映射,鲸鱼娘插画作为环境壁纸衬于磨砂玻璃界面之后,并通过主题变更守卫保持主题固定。" + }, + "npm": null, + "tarball": "https://github.com/ZHOUcourier/dsh-theme-whalegirl/releases/latest/download/dsh-theme-whalegirl.tgz", + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/ZHOUcourier/dsh-theme-whalegirl/releases/latest/download/dsh-theme-whalegirl.tgz\"", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/ZHOUcourier/dsh-theme-whalegirl/HEAD/assets/previews/preview.png", + "https://raw.githubusercontent.com/ZHOUcourier/dsh-theme-whalegirl/HEAD/assets/previews/preview-full.png" + ] + }, + { + "name": "pixel-skin", + "owner": "zhuifengqug", + "url": "https://github.com/zhuifengqug/pixel-skin", + "page": "https://awesome-dsh-plugin.com/p/zhuifengqug/pixel-skin/", + "category": "theme", + "description": { + "en": "Pokémon-style skin for DSH Web with GBA frames, HP-bar status, effort level panel, and customizable color palettes.", + "zh": "宝可梦风格 DSH Web 皮肤,含 GBA 双边框、HP 条状态、推理等级面板与可自定义配色。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:zhuifengqug/pixel-skin", + "added": "2026-08-24" + }, + { + "name": "dsh-oh-my-theme", + "owner": "zhxqc", + "url": "https://github.com/zhxqc/dsh-oh-my-theme", + "page": "https://awesome-dsh-plugin.com/p/zhxqc/dsh-oh-my-theme/", + "category": "theme", + "description": { + "en": "Web theme and file workspace plugin with global typography controls, @file mentions, a project file tree, Markdown preview, and a resizable side panel.", + "zh": "DSH Web 主题与文件工作台:全局字体设置、@ 文件引用、项目文件树、Markdown 预览和可拖拽侧边面板。" + }, + "npm": "dsh-oh-my-theme", + "stars": 7, + "downloads": 1344, + "install": "dsh plugin --profile web add dsh-oh-my-theme", + "added": "2026-08-15" + }, + { + "name": "dsh-ivory", + "owner": "ZJUZhiyuCai", + "url": "https://github.com/ZJUZhiyuCai/dsh-ivory", + "page": "https://awesome-dsh-plugin.com/p/ZJUZhiyuCai/dsh-ivory/", + "category": "theme", + "description": { + "en": "Warm light and dark theme for DSH Web with responsive layout, bilingual settings, safe Markdown preview, per-block copy controls, and no telemetry.", + "zh": "DSH Web 暖中性色明暗主题,支持响应式布局、双语设置、安全 Markdown 预览、内容块复制与无遥测运行。" + }, + "npm": "dsh-ivory", + "stars": 2, + "downloads": 1213, + "install": "dsh plugin --profile web add dsh-ivory", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/ZJUZhiyuCai/dsh-ivory/main/assets/hero-light.png", + "https://raw.githubusercontent.com/ZJUZhiyuCai/dsh-ivory/main/assets/hero-dark.png", + "https://raw.githubusercontent.com/ZJUZhiyuCai/dsh-ivory/main/assets/conversation-light.png", + "https://raw.githubusercontent.com/ZJUZhiyuCai/dsh-ivory/main/assets/mobile-light.png" + ] + }, + { + "name": "dsh-openai-oauth", + "owner": "AdonisSheldon", + "url": "https://github.com/AdonisSheldon/dsh-openai-oauth", + "page": "https://awesome-dsh-plugin.com/p/AdonisSheldon/dsh-openai-oauth/", + "category": "model", + "description": { + "en": "Connect a ChatGPT account to Codex models in DeepSeek Harness through browser PKCE or device-code OAuth, with automatic token refresh and Web or headless login.", + "zh": "通过浏览器 PKCE 或设备码 OAuth 将 ChatGPT 账户接入 DeepSeek Harness 的 Codex 模型,并支持自动刷新令牌以及 Web 或无头登录。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:AdonisSheldon/dsh-openai-oauth", + "added": "2026-08-21" + }, + { + "name": "dsh-adaptive-model-router", + "owner": "alaxrpg", + "url": "https://github.com/alaxrpg/dsh-adaptive-model-router", + "page": "https://awesome-dsh-plugin.com/p/alaxrpg/dsh-adaptive-model-router/", + "category": "model", + "description": { + "en": "Adaptive model discovery, evaluation, tiering, and subagent routing for DeepSeek Harness.", + "zh": "面向 DeepSeek Harness 的自适应模型发现、评估、定级与子代理路由插件。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:alaxrpg/dsh-adaptive-model-router", + "added": "2026-08-26" + }, + { + "name": "dsh-agy-link", + "owner": "amlyczz", + "url": "https://github.com/amlyczz/dsh-agy-link", + "page": "https://awesome-dsh-plugin.com/p/amlyczz/dsh-agy-link/", + "category": "model", + "description": { + "en": "Google Antigravity (agy CLI) models for DSH — streaming chat with Gemini/Claude/GPT-OSS subscriptions, native tool cards, thinking turns, and in-GUI Google OAuth login.", + "zh": "将 Google Antigravity (agy CLI) 接入 DSH:无 API Key 使用 Gemini/Claude/GPT-OSS 订阅模型,支持流式对话、原生工具卡片、思考轮次注记及 Web 界面 Google OAuth 扫码登录。" + }, + "npm": "dsh-agy-link", + "stars": 19, + "downloads": 4733, + "install": "dsh plugin --profile web add dsh-agy-link", + "added": "2026-08-20" + }, + { + "name": "dsh-proactive", + "owner": "beijingwahw", + "url": "https://github.com/beijingwahw/dsh-proactive", + "page": "https://awesome-dsh-plugin.com/p/beijingwahw/dsh-proactive/", + "category": "model", + "description": { + "en": "Proactive multi-model scheduling plugin: multi-source signal intake with dedup and urgency ranking, a four-way execute/defer/dismiss/ask-user decision engine, DAG plan generation with parallel model execution, quality reflection with automatic retry and model switch, long-term memory for task patterns and lessons, and strategy evolution via genetic algorithms with sandboxed evaluation and canary rollout.", + "zh": "主动式多模型协同调度插件:多源信号接入(去重与紧急度排序),execute/defer/dismiss/ask-user 四级决策引擎,DAG 计划生成与多模型并行执行,质量反思自动重试与切换模型,任务模式与经验教训长期记忆,以及遗传算法策略进化(沙盒评估、金丝雀发布)。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:beijingwahw/dsh-proactive", + "added": "2026-08-23" + }, + { + "name": "dsh-tier-router", + "owner": "BruceLanLan", + "url": "https://github.com/BruceLanLan/dsh-tier-router", + "page": "https://awesome-dsh-plugin.com/p/BruceLanLan/dsh-tier-router/", + "category": "model", + "description": { + "en": "Two-tier model routing: a strong tier plans, advises and reviews while a cheap tier implements, with plan-mode-aware auto routing, a high-impact escalation guard, failure auto-escalation, and subagent tiering.", + "zh": "双档模型路由:强档负责规划/咨询/评审、弱档负责实现;含计划模式自动路由、高危操作守卫、失败自动升级与子代理分层。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:BruceLanLan/dsh-tier-router", + "added": "2026-08-15" + }, + { + "name": "dsh-llm-fallbacks", + "owner": "btspoony", + "url": "https://github.com/btspoony/dsh-llm-fallbacks", + "page": "https://awesome-dsh-plugin.com/p/btspoony/dsh-llm-fallbacks/", + "category": "model", + "description": { + "en": "Role-based LLM retry & fallback strategies.", + "zh": "基于角色的模型重试与备用策略。" + }, + "npm": null, + "stars": 17, + "downloads": null, + "install": "dsh plugin --profile web add github:btspoony/dsh-llm-fallbacks", + "added": "2026-08-13" + }, + { + "name": "dsh-workbuddy-connect", + "owner": "corrinehu", + "url": "https://github.com/corrinehu/dsh-workbuddy-connect", + "page": "https://awesome-dsh-plugin.com/p/corrinehu/dsh-workbuddy-connect/", + "category": "model", + "description": { + "en": "Brings the models in the WorkBuddy desktop app straight into DeepSeek Harness — zero configuration in the DSH chat.", + "zh": "将 WorkBuddy 桌面 App 包含的模型自动接入 DeepSeek Harness,在 DSH 对话窗口里零配置使用。" + }, + "npm": "dsh-workbuddy-connect", + "stars": 11, + "downloads": 350, + "install": "dsh plugin --profile web add dsh-workbuddy-connect", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/corrinehu/dsh-workbuddy-connect/main/assets/1.png", + "https://raw.githubusercontent.com/corrinehu/dsh-workbuddy-connect/main/assets/2.png", + "https://raw.githubusercontent.com/corrinehu/dsh-workbuddy-connect/main/assets/3.png" + ] + }, + { + "name": "dsh-plugins#gen-limit", + "owner": "CREAIT-nl", + "url": "https://github.com/CREAIT-nl/dsh-plugins/tree/main/gen-limit", + "page": "https://awesome-dsh-plugin.com/p/CREAIT-nl/dsh-plugins--gen-limit/", + "category": "model", + "description": { + "en": "Per provider/model generation concurrency, with a settings card: a cap on how many sessions may stream at once, enforced at the stream and again at subagent spawn, where a fan-out past the cap waits in a FIFO queue instead of failing.", + "zh": "按提供方/模型限制并发生成,并附带设置卡片:限定同时进行流式生成的会话数,在流式调用与子代理派生两处强制执行;超出上限的扇出会进入 FIFO 队列等待,而不是直接失败。" + }, + "npm": "@creait/dsh-gen-limit", + "stars": 3, + "downloads": 70, + "install": "dsh plugin --profile web add @creait/dsh-gen-limit", + "added": "2026-08-24" + }, + { + "name": "dsh-plugins#think-level", + "owner": "CREAIT-nl", + "url": "https://github.com/CREAIT-nl/dsh-plugins/tree/main/think-level", + "page": "https://awesome-dsh-plugin.com/p/CREAIT-nl/dsh-plugins--think-level/", + "category": "model", + "description": { + "en": "A thinking level stored per provider and model, so reasoning effort outlives the session it was set in and also reaches subagents, which dsh never asks. The in-session picker still wins, and a model with no row keeps its provider default.", + "zh": "按提供方与模型保存思考等级:推理强度不再只属于设置它的那次会话,还会应用到 dsh 从不询问的子代理。会话内的手动选择依然优先,没有配置的模型保持提供方默认值。" + }, + "npm": "@creait/dsh-think-level", + "stars": 3, + "downloads": 70, + "install": "dsh plugin --profile web add @creait/dsh-think-level", + "added": "2026-08-24" + }, + { + "name": "dsh-plugin-dit", + "owner": "cuboteam", + "url": "https://github.com/cuboteam/dsh-plugin-dit", + "page": "https://awesome-dsh-plugin.com/p/cuboteam/dsh-plugin-dit/", + "category": "model", + "description": { + "en": "DIT.ai provider bundle for DeepSeek Harness: installs 29 OpenAI Chat Completions models and 10 Anthropic Messages models from one DIT_API_KEY, with protocol-specific thinking configuration.", + "zh": "DIT.ai 的 DeepSeek Harness 模型接入 bundle:使用一个 DIT_API_KEY 安装 29 个 OpenAI Chat Completions 模型和 10 个 Anthropic Messages 模型,并按协议配置 thinking。" + }, + "npm": "dsh-plugin-dit", + "stars": 2, + "downloads": 575, + "install": "dsh plugin --profile web add dsh-plugin-dit", + "added": "2026-08-18" + }, + { + "name": "DSH-Subagent-Model-Router", + "owner": "CypherNaught-0x", + "url": "https://github.com/CypherNaught-0x/DSH-Subagent-Model-Router", + "page": "https://awesome-dsh-plugin.com/p/CypherNaught-0x/DSH-Subagent-Model-Router/", + "category": "model", + "description": { + "en": "Routes delegated subagent work to configured models: a subagent_model tool whose model enum is the configured aliases, per-route tags and routing guidance injected into the tool schema and system prompt, a wait-for-subagents join tool returning terminal results from outstanding background children, active-model chips in the subagent header and catalog rows, and a Web settings page, namespace-scoped configuration tool, and guided setup skill.", + "zh": "把委派给子代理的任务路由到配置好的模型:subagent_model 工具的 model 枚举即配置的别名,每条路由的标签与使用说明会注入工具 schema 与系统提示;wait-for-subagents 汇合工具等待未完成的可续跑后台子代理并返回其最终结果;在子代理头部与目录行显示当前模型标签;另有 Web 设置页、命名空间内的配置工具与引导设置技能用于管理路由。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:CypherNaught-0x/DSH-Subagent-Model-Router", + "added": "2026-08-19" + }, + { + "name": "dsh-agy-provider", + "owner": "darkings", + "url": "https://github.com/darkings/dsh-agy-provider", + "page": "https://awesome-dsh-plugin.com/p/darkings/dsh-agy-provider/", + "category": "model", + "description": { + "en": "DSH model provider for the locally authenticated AGY CLI: Gemini/Claude model discovery, independent reasoning effort, DSH-owned tools, bounded context and tool-result handling, and Windows-safe streaming. Report problems in GitHub Issues: https://github.com/darkings/dsh-agy-provider/issues.", + "zh": "将本机已登录的 AGY CLI 接入 DSH 作为模型 Provider:支持 Gemini/Claude 模型发现、独立推理强度、DSH-owned 工具、受限上下文与工具结果管理、Windows 安全流式传输;如遇问题,请在 GitHub 提交 Issue:https://github.com/darkings/dsh-agy-provider/issues。" + }, + "npm": "dsh-agy-provider", + "stars": 2, + "downloads": 1578, + "install": "dsh plugin --profile web add dsh-agy-provider", + "added": "2026-08-24" + }, + { + "name": "dsh-kiro", + "owner": "dat-lequoc", + "url": "https://github.com/dat-lequoc/dsh-kiro", + "page": "https://awesome-dsh-plugin.com/p/dat-lequoc/dsh-kiro/", + "category": "model", + "description": { + "en": "Kiro model provider with Builder ID, Identity Center, Google, GitHub and credential-import sign-in, automatic token and profile refresh, account usage, live model discovery, selectable reasoning efforts, streaming tool calls, provider stop-reason and context-overflow handling, and token accounting that uses Kiro's exact counters when it sends them and otherwise scales the context-usage percentage it does send by the model's advertised window.", + "zh": "Kiro 模型提供商,支持 Builder ID、Identity Center、Google、GitHub 与凭据导入登录、token 和 profile 自动刷新、账户用量、实时模型发现、推理档位选择、流式工具调用、上游结束原因与上下文溢出处理;token 统计在 Kiro 提供精确计数时直接采用,否则用它确实会发送的上下文占用百分比乘以模型公布的窗口换算。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:dat-lequoc/dsh-kiro", + "added": "2026-08-28" + }, + { + "name": "dsh-chinese-mode", + "owner": "dawnliming", + "url": "https://github.com/dawnliming/dsh-chinese-mode", + "page": "https://awesome-dsh-plugin.com/p/dawnliming/dsh-chinese-mode/", + "category": "model", + "description": { + "en": "Global Chinese mode: a compact pill switch beside the composer that injects a language requirement into any preset's system prompt (complete personas excepted); reply, thinking and tool output each toggle between Chinese and English, and anchored presets are supported.", + "zh": "全局中文模式:输入框旁的紧凑胶囊开关,开启后向任意预设(完整 persona 除外)的系统提示注入语言要求;回复、思考、工具区域可分别切换中英文,并兼容锚定预设。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:dawnliming/dsh-chinese-mode", + "added": "2026-08-16" + }, + { + "name": "dsh-subagent-default-model#plugin", + "owner": "dingminhua", + "url": "https://github.com/dingminhua/dsh-subagent-default-model/tree/main/plugin", + "page": "https://awesome-dsh-plugin.com/p/dingminhua/dsh-subagent-default-model--plugin/", + "category": "model", + "description": { + "en": "Configurable default model for subagent delegations via settings.yaml, with single-model and multi-model round-robin or random strategies.", + "zh": "通过 settings.yaml 为子代理派发配置默认模型,支持单模型与多模型轮换/随机分配策略。" + }, + "npm": "dsh-subagent-default-model", + "stars": 2, + "downloads": 207, + "install": "dsh plugin --profile web add dsh-subagent-default-model", + "added": "2026-08-18" + }, + { + "name": "dsh-thinking-levels", + "owner": "drscrewdriver", + "url": "https://github.com/drscrewdriver/dsh-thinking-levels", + "page": "https://awesome-dsh-plugin.com/p/drscrewdriver/dsh-thinking-levels/", + "category": "model", + "description": { + "en": "DSH thinking-level (reasoning effort) control: auto-adjust reasoning strength per tool round with optional manual lock and per-tool timing telemetry.", + "zh": "DSH 思考级别(reasoning effort)控制:按工具调用轮次自动调整推理强度,支持手动锁定与单工具耗时遥测。" + }, + "npm": "dsh-thinking-levels", + "stars": 4, + "downloads": 1370, + "install": "dsh plugin --profile web add dsh-thinking-levels", + "added": "2026-08-23" + }, + { + "name": "llm-adaptive", + "owner": "dylan121322", + "url": "https://github.com/dylan121322/llm-adaptive", + "page": "https://awesome-dsh-plugin.com/p/dylan121322/llm-adaptive/", + "category": "model", + "description": { + "en": "Adaptive model routing: per-request complexity classification with automatic provider routing.", + "zh": "自适应模型路由:请求级复杂度分类,按配置链自动选择后端 provider。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:dylan121322/llm-adaptive", + "added": "2026-08-14" + }, + { + "name": "dsh-http-proxy", + "owner": "elizax", + "url": "https://github.com/elizax/dsh-http-proxy", + "page": "https://awesome-dsh-plugin.com/p/elizax/dsh-http-proxy/", + "category": "model", + "description": { + "en": "Wraps globalThis.fetch to route DeepSeek Harness model-API requests through an HTTP/SOCKS proxy by target domain, leaving all other requests on the direct connection, without modifying Harness source.", + "zh": "把 globalThis.fetch 包一层,按目标域名让 DeepSeek Harness 的模型 API 请求走 HTTP/SOCKS 代理、其它请求保持直连,全程不改 Harness 源码。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:elizax/dsh-http-proxy", + "added": "2026-08-27" + }, + { + "name": "DSH-CODEX-SUBSCRIPTION-POOL", + "owner": "Eve-146T", + "url": "https://github.com/Eve-146T/DSH-CODEX-SUBSCRIPTION-POOL", + "page": "https://awesome-dsh-plugin.com/p/Eve-146T/DSH-CODEX-SUBSCRIPTION-POOL/", + "category": "model", + "description": { + "en": "Use your ChatGPT/Codex subscriptions in DSH, with image generation and web search support.", + "zh": "在 DSH 中使用你的 ChatGPT/Codex 订阅,并支持图像生成和 Web 搜索。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:Eve-146T/DSH-CODEX-SUBSCRIPTION-POOL", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/Eve-146T/DSH-CODEX-SUBSCRIPTION-POOL/main/assets/readme/accounts-demo.png" + ] + }, + { + "name": "dsh-llm-longcat", + "owner": "ffyuuu", + "url": "https://github.com/ffyuuu/dsh-llm-longcat", + "page": "https://awesome-dsh-plugin.com/p/ffyuuu/dsh-llm-longcat/", + "category": "model", + "description": { + "en": "LongCat-2.0 LLM adapter: 1M context, binary thinking mode, tool calling, and streaming over the OpenAI-compatible endpoint.", + "zh": "LongCat-2.0 模型适配器:100 万上下文、二元思考模式、工具调用,基于 OpenAI 兼容接口的流式输出。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:ffyuuu/dsh-llm-longcat", + "added": "2026-08-21" + }, + { + "name": "keyringseam", + "owner": "fieldnote-ops", + "url": "https://github.com/fieldnote-ops/keyringseam", + "page": "https://awesome-dsh-plugin.com/p/fieldnote-ops/keyringseam/", + "category": "model", + "description": { + "en": "macOS Keychain credential provider that replaces the local-file provider and uses a signed, notarized universal helper.", + "zh": "macOS Keychain 凭据提供器:替换本地文件提供器,并使用已签名、公证的通用二进制辅助程序。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:fieldnote-ops/keyringseam", + "added": "2026-08-15" + }, + { + "name": "dsh-codex-connect", + "owner": "franksong2702", + "url": "https://github.com/franksong2702/dsh-codex-connect", + "page": "https://awesome-dsh-plugin.com/p/franksong2702/dsh-codex-connect/", + "category": "model", + "description": { + "en": "Connect ChatGPT OAuth and OpenAI Codex models to DeepSeek Harness, with opt-in search and image tools.", + "zh": "通过 ChatGPT OAuth 将 OpenAI Codex 模型接入 DeepSeek Harness,并提供可选的搜索与图片工具。" + }, + "npm": "dsh-codex-connect", + "stars": 47, + "downloads": 6756, + "install": "dsh plugin --profile web add dsh-codex-connect", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/en/plugin-entry.jpg", + "https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/en/oauth-status.jpg", + "https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/en/plugin-configuration.jpg", + "https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/en/model-selector.jpg", + "https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/zh/hero.jpg", + "https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/zh/plugin-entry.jpg", + "https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/zh/oauth-status.jpg", + "https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/zh/plugin-configuration.jpg" + ] + }, + { + "name": "claude-in-dsh", + "owner": "GeekRicardo", + "url": "https://github.com/GeekRicardo/claude-in-dsh", + "page": "https://awesome-dsh-plugin.com/p/GeekRicardo/claude-in-dsh/", + "category": "model", + "description": { + "en": "Hand a DSH web session to the locally installed Claude Code CLI: a per-session engine selector (DSH | Claude Code), Claude models and reasoning effort in the model seat, Claude permission modes in place of the access-mode selector, and permission requests, AskUserQuestion and ExitPlanMode routed to the native DSH approval, question and plan-review cards. The stream is written as native DSH session events, so the built-in transcript, tool cards and nested subagents render it; Claude slash commands merge into the command palette; the CLI is held by a detached broker, so plugin updates and DSH restarts do not interrupt a running turn.", + "zh": "把一个 dsh web 会话交给本机 Claude Code CLI 驱动:输入框内的引擎选择器(DSH | Claude Code)按会话切换,模型座换成 Claude 的模型与 reasoning effort,访问模式换成 Claude 权限档;权限请求、AskUserQuestion、ExitPlanMode 分别接到 dsh 原生的审批卡、提问卡与计划审核卡。Claude 的流写成 dsh 原生会话事件,由内建转录、工具卡片与嵌套子 agent 渲染;斜杠命令并入命令面板;CLI 由独立 broker 持有,插件更新与 dsh 重启都不中断进行中的轮次。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:GeekRicardo/claude-in-dsh", + "added": "2026-08-28" + }, + { + "name": "dsh-sub2api", + "owner": "GodD6366", + "url": "https://github.com/GodD6366/dsh-sub2api", + "page": "https://awesome-dsh-plugin.com/p/GodD6366/dsh-sub2api/", + "category": "model", + "description": { + "en": "Connect a sub2api gateway to DeepSeek Harness: OpenAI-compatible multi-provider routes (OpenAI / Claude / Grok / Gemini) behind one base URL, with per-key model discovery, usage lookup, and global vision/image tools.", + "zh": "将 sub2api 网关接入 DeepSeek Harness:用一个 base URL 统一管理 OpenAI / Claude / Grok / Gemini 等多供应商路由,支持按 key 发现模型、用量查询与全局视觉/图像生成工具。" + }, + "npm": "@godd6366/dsh-sub2api", + "stars": 2, + "downloads": 1263, + "install": "dsh plugin --profile web add @godd6366/dsh-sub2api", + "added": "2026-08-15" + }, + { + "name": "dsh-better-reasoning-effort", + "owner": "HaoyueQin", + "url": "https://github.com/HaoyueQin/dsh-better-reasoning-effort", + "page": "https://awesome-dsh-plugin.com/p/HaoyueQin/dsh-better-reasoning-effort/", + "category": "model", + "description": { + "en": "Per-model reasoning-effort and input-modality configuration for third-party providers: suggestions fused from a model knowledge base and raw /models endpoint evidence with confidence labels, edited inside the official Models page card.", + "zh": "为第三方供应商模型按模型配置思考强度与输入模态:知识库与原始 /models 端点取证融合建议并标注置信度,在官方「模型」页卡片内直接编辑。" + }, + "npm": "dsh-better-reasoning-effort", + "stars": 4, + "downloads": 943, + "install": "dsh plugin --profile web add dsh-better-reasoning-effort", + "added": "2026-08-23", + "screenshots": [ + "https://raw.githubusercontent.com/HaoyueQin/dsh-better-reasoning-effort/HEAD/docs/market-card.png" + ] + }, + { + "name": "dsh-llm-failover", + "owner": "HB00", + "url": "https://github.com/HB00/dsh-llm-failover", + "page": "https://awesome-dsh-plugin.com/p/HB00/dsh-llm-failover/", + "category": "model", + "description": { + "en": "Automatic provider failover on rate limits or quota exhaustion: switch between providers with configurable per-provider models, a cooldown, and a permanent last-entry fallback.", + "zh": "限流或配额耗尽时自动切换模型提供方:可按提供方配置模型,带冷却机制与永久最终兜底。" + }, + "npm": "dsh-llm-failover", + "stars": 1, + "downloads": 264, + "install": "dsh plugin --profile web add dsh-llm-failover", + "added": "2026-08-18" + }, + { + "name": "dsh-models-radar", + "owner": "hi-fangj", + "url": "https://github.com/hi-fangj/dsh-models-radar", + "page": "https://awesome-dsh-plugin.com/p/hi-fangj/dsh-models-radar/", + "category": "model", + "description": { + "en": "Displays CodexRadar model capability benchmarks in a Settings page and shows the selected session model's DeepSWE score beside the composer.", + "zh": "在设置页展示 CodexRadar 模型能力评测,并在输入框旁显示当前会话所选模型的 DeepSWE 分数。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:hi-fangj/dsh-models-radar", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/hi-fangj/dsh-models-radar/HEAD/docs/screenshots/settings-overview.png", + "https://raw.githubusercontent.com/hi-fangj/dsh-models-radar/HEAD/docs/screenshots/capability-popover.png" + ] + }, + { + "name": "dsh-thinking-effort", + "owner": "hytime", + "url": "https://github.com/hytime/dsh-thinking-effort", + "page": "https://awesome-dsh-plugin.com/p/hytime/dsh-thinking-effort/", + "category": "model", + "description": { + "en": "Configure reasoning-effort levels and sub-agent defaults for custom DSH models.", + "zh": "为 DSH 自定义模型配置思考档位和子 agent 默认思考强度。" + }, + "npm": "@hytime/dsh-thinking-effort", + "stars": 13, + "downloads": 1543, + "install": "dsh plugin --profile web add @hytime/dsh-thinking-effort", + "added": "2026-08-20" + }, + { + "name": "dsh-polyglot", + "owner": "Jesse-njx", + "url": "https://github.com/Jesse-njx/dsh-polyglot", + "page": "https://awesome-dsh-plugin.com/p/Jesse-njx/dsh-polyglot/", + "category": "model", + "description": { + "en": "The model switch for DSH: point it at any OpenAI-compatible endpoint, with curated free/cheap DeepSeek provider presets and automatic fallback when a free tier rate-limits you.", + "zh": "DSH 的模型切换器:指向任意 OpenAI 兼容端点,内置精选免费/低价 DeepSeek 服务商预设,免费额度限流时自动回退。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:Jesse-njx/dsh-polyglot", + "added": "2026-08-14" + }, + { + "name": "dsh-model-sync", + "owner": "jiay98528-dev", + "url": "https://github.com/jiay98528-dev/dsh-model-sync", + "page": "https://awesome-dsh-plugin.com/p/jiay98528-dev/dsh-model-sync/", + "category": "model", + "description": { + "en": "Writes live provider model lists into DSH settings and shows 5h/7d plan windows or metered balance for the current session model.", + "zh": "把各提供方线上模型列表写进 DSH 设置,输入框圆环显示当前会话模型的 5 小时/7 天窗口或按量剩余余额。" + }, + "npm": "dsh-model-sync", + "stars": 2, + "downloads": 620, + "install": "dsh plugin --profile web add dsh-model-sync", + "added": "2026-08-15" + }, + { + "name": "dsh-plugin-weaknet-adaptor", + "owner": "jiay98528-dev", + "url": "https://github.com/jiay98528-dev/dsh-plugin-weaknet-adaptor", + "page": "https://awesome-dsh-plugin.com/p/jiay98528-dev/dsh-plugin-weaknet-adaptor/", + "category": "model", + "description": { + "en": "Weak-network adaptor: transparent long-backoff model-stream retries (10-min cap), local response replay cache, heartbeat auto-reconnect, and degraded-mode token economy, with a bilingual (zh/en) settings page.", + "zh": "弱网适配器:模型流式请求的长退避透明重试(上限 10 分钟)、本地响应重放缓存、心跳自动重连、降级模式 token 节流,并提供中英双语设置页。" + }, + "npm": "dsh-plugin-weaknet-adaptor", + "stars": 3, + "downloads": 1008, + "install": "dsh plugin --profile web add dsh-plugin-weaknet-adaptor", + "added": "2026-08-17" + }, + { + "name": "dsh-maestro", + "owner": "JUNQINGV587", + "url": "https://github.com/JUNQINGV587/dsh-maestro", + "page": "https://awesome-dsh-plugin.com/p/JUNQINGV587/dsh-maestro/", + "category": "model", + "description": { + "en": "Planner/executor delegation with named roles: a strong planner steers, disposable executor sub-agents (per-role routes and prompts) implement. Default role plus per-delegation override, a global reasoning-effort knob in the composer chip, and a settings page for role management.", + "zh": "规划者/执行者委派调度:高阶模型(Planner)定方向,一次性低阶执行子代理(按角色的路由与提示词)干具体活;默认角色 + 单次委派覆盖,输入框芯片快速切换角色与全局思考等级,设置页管理执行角色。" + }, + "npm": null, + "stars": 0, + "downloads": null, + "install": "dsh plugin --profile web add github:JUNQINGV587/dsh-maestro", + "added": "2026-08-25" + }, + { + "name": "dsh-provider-rate-limit", + "owner": "jyao-SUSE-power-group", + "url": "https://github.com/jyao-SUSE-power-group/dsh-provider-rate-limit", + "page": "https://awesome-dsh-plugin.com/p/jyao-SUSE-power-group/dsh-provider-rate-limit/", + "category": "model", + "description": { + "en": "Rate-limits LLM requests per provider and per model using a reservation-based token bucket, with queue or reject modes, plus gateway identity rules that rewrite user-agent and inject custom headers.", + "zh": "按供应商与模型粒度限速 LLM 请求,预约式令牌桶支持排队或拒绝两种模式,附带网关身份规则(改写 User-Agent、注入自定义请求头)。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:jyao-SUSE-power-group/dsh-provider-rate-limit", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/jyao-SUSE-power-group/dsh-provider-rate-limit/HEAD/assets/screenshots/settings-card.png", + "https://raw.githubusercontent.com/jyao-SUSE-power-group/dsh-provider-rate-limit/HEAD/assets/screenshots/composer-dock-stats.png", + "https://raw.githubusercontent.com/jyao-SUSE-power-group/dsh-provider-rate-limit/HEAD/assets/screenshots/settings-config-1.png", + "https://raw.githubusercontent.com/jyao-SUSE-power-group/dsh-provider-rate-limit/HEAD/assets/screenshots/settings-config-2.png" + ] + }, + { + "name": "dsh-everything-oauth", + "owner": "kam74515-boop", + "url": "https://github.com/kam74515-boop/dsh-everything-oauth", + "page": "https://awesome-dsh-plugin.com/p/kam74515-boop/dsh-everything-oauth/", + "category": "model", + "description": { + "en": "Import local Codex, Grok, Claude, OpenCode, and CC Switch logins into DSH; pick sources and enable models in Settings.", + "zh": "把本机 Codex / Grok / Claude / OpenCode / CC Switch 登录态导入 DSH,在设置里自选来源并启用模型。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:kam74515-boop/dsh-everything-oauth", + "added": "2026-08-14" + }, + { + "name": "dsh-claude-cli", + "owner": "katsos", + "url": "https://github.com/katsos/dsh-claude-cli", + "page": "https://awesome-dsh-plugin.com/p/katsos/dsh-claude-cli/", + "category": "model", + "description": { + "en": "LLM provider that runs the locally installed Claude Code CLI as the model backend, so requests go through an existing Claude subscription instead of a metered API key; native tool calls are bridged over MCP.", + "zh": "LLM 供应商:把本机已安装的 Claude Code CLI 作为模型后端,请求走已订阅的 Claude 账号,无需按量计费的 API key;原生工具调用经 MCP 桥接。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:katsos/dsh-claude-cli", + "added": "2026-08-15" + }, + { + "name": "dsh-plugin-subhub", + "owner": "kinoward", + "url": "https://github.com/kinoward/dsh-plugin-subhub", + "page": "https://awesome-dsh-plugin.com/p/kinoward/dsh-plugin-subhub/", + "category": "model", + "description": { + "en": "Use third-party subscription accounts in DeepSeek Harness: chat, image understanding, image generation, and image editing with the models your subscription covers, with available models and reasoning levels synced from your account; currently supports OpenAI/ChatGPT subscriptions, more providers planned.", + "zh": "用第三方订阅账户在 DeepSeek Harness 中使用订阅覆盖的模型:文字对话、图片理解、图片生成与图片编辑,可用模型与推理等级随账户自动同步;当前支持 OpenAI / ChatGPT 订阅,更多订阅服务规划中。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:kinoward/dsh-plugin-subhub", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/kinoward/dsh-plugin-subhub/main/assets/models-en-light.png", + "https://raw.githubusercontent.com/kinoward/dsh-plugin-subhub/main/assets/models-zh-light.png", + "https://raw.githubusercontent.com/kinoward/dsh-plugin-subhub/main/assets/settings-loggedin-en-light.png", + "https://raw.githubusercontent.com/kinoward/dsh-plugin-subhub/main/assets/settings-loggedin-zh-light.png" + ] + }, + { + "name": "dsh-antigravity", + "owner": "LiZhenNet", + "url": "https://github.com/LiZhenNet/dsh-antigravity", + "page": "https://awesome-dsh-plugin.com/p/LiZhenNet/dsh-antigravity/", + "category": "model", + "description": { + "en": "Google Antigravity / Cloud Code Assist model provider with quota management and model selector.", + "zh": "Google Antigravity / Cloud Code Assist 模型提供商插件,支持配额查看与模型选择管理。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:LiZhenNet/dsh-antigravity", + "added": "2026-08-20", + "screenshots": [ + "https://raw.githubusercontent.com/LiZhenNet/dsh-antigravity/main/assets/images/settings-signed-in.png", + "https://raw.githubusercontent.com/LiZhenNet/dsh-antigravity/main/assets/images/chat-model-picker.png", + "https://raw.githubusercontent.com/LiZhenNet/dsh-antigravity/main/assets/images/settings-not-signed-in.png" + ] + }, + { + "name": "oh-my-dsh", + "owner": "llmpolska", + "url": "https://github.com/llmpolska/oh-my-dsh", + "page": "https://awesome-dsh-plugin.com/p/llmpolska/oh-my-dsh/", + "category": "model", + "description": { + "en": "Tiered model routing for DeepSeek Harness: think/build model tiers, vision delegation (the vision model only describes images; the working model acts), image generation, and a high-impact guard.", + "zh": "面向 DeepSeek Harness 的分级模型路由:think/build 模型分级、视觉委派(视觉模型只负责描述图片,工作模型负责执行操作)、图片生成,以及高影响操作守卫。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:llmpolska/oh-my-dsh", + "added": "2026-08-18" + }, + { + "name": "dsh-coding-subscription-oauth", + "owner": "lninghaha", + "url": "https://github.com/lninghaha/dsh-coding-subscription-oauth", + "page": "https://awesome-dsh-plugin.com/p/lninghaha/dsh-coding-subscription-oauth/", + "category": "model", + "description": { + "en": "Coding-subscription OAuth for SuperGrok/Grok Build, ChatGPT Plus Codex, Kimi Code and Claude Code, plus an opt-in loopback OpenAI/Anthropic gateway: local login without pasting tokens, AUTH refresh-and-retry, grok-4.6 xhigh, and API-key route coexistence.", + "zh": "编码订阅 OAuth,支持 SuperGrok / Grok Build、ChatGPT Plus Codex、Kimi Code、Claude Code,并提供可选 loopback OpenAI/Anthropic 网关:本地登录无需粘贴 token,支持 AUTH 刷新重试、grok-4.6 xhigh,并可与 API-key 路由共存。" + }, + "npm": "dsh-coding-subscription-oauth", + "stars": 15, + "downloads": 2947, + "install": "dsh plugin --profile web add dsh-coding-subscription-oauth", + "added": "2026-08-18" + }, + { + "name": "dsh-llm-github-copilot", + "owner": "lujianjun19", + "url": "https://github.com/lujianjun19/dsh-llm-github-copilot", + "page": "https://awesome-dsh-plugin.com/p/lujianjun19/dsh-llm-github-copilot/", + "category": "model", + "description": { + "en": "GitHub Copilot LLM adapter: OAuth device-flow sign-in, live model discovery from the Copilot API, vision support for image-capable models (gpt-4.1, gpt-4o), and two wire protocols (Chat Completions and Responses API) with automatic endpoint routing per model.", + "zh": "GitHub Copilot LLM 适配器:OAuth 设备码登录、从 Copilot API 动态发现模型、视觉模型图片输入支持(gpt-4.1、gpt-4o),以及 Chat Completions 与 Responses 双协议,按模型自动路由。" + }, + "npm": "@lujianjun19/dsh-llm-github-copilot", + "stars": 4, + "downloads": 2222, + "install": "dsh plugin --profile web add @lujianjun19/dsh-llm-github-copilot", + "added": "2026-08-18" + }, + { + "name": "dsh-grok-kit", + "owner": "MaRi23333", + "url": "https://github.com/MaRi23333/dsh-grok-kit", + "page": "https://awesome-dsh-plugin.com/p/MaRi23333/dsh-grok-kit/", + "category": "model", + "description": { + "en": "Unofficial Grok integration for eligible SuperGrok or X Premium subscriptions: grok-4.6 chat with main-loop web/X search, shared Grok CLI OAuth, configurable model visibility, and Imagine images saved to an attachment or chosen path.", + "zh": "面向符合资格的 SuperGrok 或 X Premium 订阅的非官方 Grok 集成:支持 grok-4.6 对话、主循环网页/X 搜索、共用 Grok CLI OAuth、可配模型显示,以及将 Imagine 图片保存到附件库或指定路径。" + }, + "npm": "dsh-grok-kit", + "stars": 0, + "downloads": 0, + "install": "dsh plugin --profile web add dsh-grok-kit", + "added": "2026-08-25" + }, + { + "name": "dsh-commandcode-provider", + "owner": "Mars-Sea", + "url": "https://github.com/Mars-Sea/dsh-commandcode-provider", + "page": "https://awesome-dsh-plugin.com/p/Mars-Sea/dsh-commandcode-provider/", + "category": "model", + "description": { + "en": "Unofficial Command Code LLM provider: registers a `commandcode` route with a live model catalog and reasoning-effort support.", + "zh": "非官方 Command Code 模型接入插件:注册 `commandcode` 路由,带实时模型目录与推理强度支持。" + }, + "npm": "@mars-sea/dsh-commandcode-provider", + "stars": 103, + "downloads": 5248, + "install": "dsh plugin --profile web add @mars-sea/dsh-commandcode-provider", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/Mars-Sea/dsh-commandcode-provider/main/assets/screenshots/model-picker.png", + "https://raw.githubusercontent.com/Mars-Sea/dsh-commandcode-provider/main/assets/screenshots/usage-dashboard.png" + ] + }, + { + "name": "dsh-modellix", + "owner": "Modellix", + "url": "https://github.com/Modellix/dsh-modellix", + "page": "https://awesome-dsh-plugin.com/p/Modellix/dsh-modellix/", + "category": "model", + "description": { + "en": "Modellix integration for DSH: schema-driven image, video and audio generation in a split Design workspace, a live LLM model catalog, and native web search and fetch providers through one API key.", + "zh": "将 Modellix 接入 DSH:使用一个 API Key 在分栏 Design 工作区中提供基于 Schema 的图片、视频与音频生成、实时 LLM 模型目录,以及原生网页搜索与抓取 Provider。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Modellix/dsh-modellix", + "added": "2026-08-27" + }, + { + "name": "dsh-openrouter-providers", + "owner": "MoRanYue", + "url": "https://github.com/MoRanYue/dsh-openrouter-providers", + "page": "https://awesome-dsh-plugin.com/p/MoRanYue/dsh-openrouter-providers/", + "category": "model", + "description": { + "en": "Route OpenRouter requests through a configured provider list and quantization cap, injected as provider.only/order with allow_fallbacks, plus provider.quantizations.", + "zh": "配置 OpenRouter 请求使用的提供商列表与量化位数上限,注入 provider.only/order(allow_fallbacks)与 provider.quantizations 路由参数。" + }, + "npm": "dsh-openrouter-providers", + "stars": 1, + "downloads": 1121, + "install": "dsh plugin --profile web add dsh-openrouter-providers", + "added": "2026-08-23" + }, + { + "name": "dsh-llm-codex", + "owner": "NOirBRight", + "url": "https://github.com/NOirBRight/dsh-llm-codex", + "page": "https://awesome-dsh-plugin.com/p/NOirBRight/dsh-llm-codex/", + "category": "model", + "description": { + "en": "ChatGPT Codex subscription chat: official OAuth, a Sol/Terra/Luna catalog with Fast rows, live usage limits, and optional search and view_image tools.", + "zh": "ChatGPT Codex 订阅聊天:官方 OAuth 登录,Sol/Terra/Luna 目录含 Fast 行,实时额度,以及可选的搜索与 view_image 工具。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:NOirBRight/dsh-llm-codex", + "added": "2026-08-18" + }, + { + "name": "dsh-llm-cursor", + "owner": "NOirBRight", + "url": "https://github.com/NOirBRight/dsh-llm-cursor", + "page": "https://awesome-dsh-plugin.com/p/NOirBRight/dsh-llm-cursor/", + "category": "model", + "description": { + "en": "Unofficial Cursor subscription chat: Host-owned CLI session login, a displayed model catalog, and subscription usage rails. Cursor staff treat this class of private-client usage as against ToS.", + "zh": "非官方 Cursor 订阅聊天:Host 持有 CLI 会话登录,可编辑展示用模型目录,并显示订阅额度。Cursor 员工认定此类私有客户端用法违反 ToS。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:NOirBRight/dsh-llm-cursor", + "added": "2026-08-18" + }, + { + "name": "dsh-llm-grok", + "owner": "NOirBRight", + "url": "https://github.com/NOirBRight/dsh-llm-grok", + "page": "https://awesome-dsh-plugin.com/p/NOirBRight/dsh-llm-grok/", + "category": "model", + "description": { + "en": "xAI Grok subscription chat: SuperGrok or X Premium+ OAuth, a displayed model subset, Responses through the Grok CLI proxy, and always-on server-side web_search and x_search.", + "zh": "xAI Grok 订阅聊天:用 SuperGrok 或 X Premium+ 的 OAuth 登录,选择要显示的模型子集,经 Grok CLI 代理走 Responses,并始终开启服务端 web_search 与 x_search。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:NOirBRight/dsh-llm-grok", + "added": "2026-08-18" + }, + { + "name": "dsh-llm-ollama", + "owner": "NOirBRight", + "url": "https://github.com/NOirBRight/dsh-llm-ollama", + "page": "https://awesome-dsh-plugin.com/p/NOirBRight/dsh-llm-ollama/", + "category": "model", + "description": { + "en": "Ollama Cloud native chat adapter: direct NDJSON translation of the Ollama /api/chat wire format, model discovery with context windows and capabilities, and web search/fetch providers under the ollama-cloud id, plus a Web settings card.", + "zh": "Ollama Cloud 原生聊天适配器:直连 NDJSON 翻译 Ollama /api/chat 协议,模型发现带上下文窗口与能力信息,并注册 web 搜索/抓取 provider,附 Web 设置卡片。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:NOirBRight/dsh-llm-ollama", + "added": "2026-08-15" + }, + { + "name": "Qwen-MM-Plugins", + "owner": "omdsh-dev", + "url": "https://github.com/omdsh-dev/Qwen-MM-Plugins", + "page": "https://awesome-dsh-plugin.com/p/omdsh-dev/Qwen-MM-Plugins/", + "category": "model", + "description": { + "en": "Qwen multi-modal plugin support.", + "zh": "Qwen 多模态插件支持。" + }, + "npm": null, + "stars": 0, + "downloads": null, + "install": "dsh plugin --profile web add github:omdsh-dev/Qwen-MM-Plugins", + "added": "2026-08-13" + }, + { + "name": "dsh-model-gate", + "owner": "OPaimon", + "url": "https://github.com/OPaimon/dsh-model-gate", + "page": "https://awesome-dsh-plugin.com/p/OPaimon/dsh-model-gate/", + "category": "model", + "description": { + "en": "Single-model disable gate: hides denied models from DSH catalogs, blocks direct dispatch with an in-protocol error chunk, and exposes a settings UI for the denylist.", + "zh": "提供模型粒度的启用禁用机制:从 DSH 模型目录隐藏被禁用的模型,以协议内错误块拦截直接分发请求,并提供UI设置界面。" + }, + "npm": "dsh-model-gate", + "stars": 0, + "downloads": 300, + "install": "dsh plugin --profile web add dsh-model-gate", + "added": "2026-08-25" + }, + { + "name": "dsh-codex-shim", + "owner": "OpenTritium", + "url": "https://github.com/OpenTritium/dsh-codex-shim", + "page": "https://awesome-dsh-plugin.com/p/OpenTritium/dsh-codex-shim/", + "category": "model", + "description": { + "en": "Simulates a Codex environment for selected model routes to improve GPT-family model tool-call success.", + "zh": "为选定的模型路由模拟 Codex 环境,以提升 GPT 系列模型的工具调用成功率。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:OpenTritium/dsh-codex-shim", + "added": "2026-08-16" + }, + { + "name": "dsh-nl-model-switch", + "owner": "passingby000", + "url": "https://github.com/passingby000/dsh-nl-model-switch", + "page": "https://awesome-dsh-plugin.com/p/passingby000/dsh-nl-model-switch/", + "category": "model", + "description": { + "en": "Switch the current DSH session's model with a natural-language sentence, on web, TUI, or IM bridges (WeChat, Feishu), without touching the panel.", + "zh": "用一句自然语言切换当前 DSH 会话模型,可在 Web、TUI 及 IM 桥接的微信、飞书等对话框中使用,无需操作面板。" + }, + "npm": "dsh-nl-model-switch", + "stars": 2, + "downloads": 327, + "install": "dsh plugin --profile web add dsh-nl-model-switch", + "added": "2026-08-21" + }, + { + "name": "dsh-plugin-thinking-api", + "owner": "qjf44", + "url": "https://github.com/qjf44/dsh-plugin-thinking-api", + "page": "https://awesome-dsh-plugin.com/p/qjf44/dsh-plugin-thinking-api/", + "category": "model", + "description": { + "en": "One-click OpenAI-compatible API (CodeBuddy/vLLM/proxies) with thinking mode, avoiding developer-role content_filter rejection.", + "zh": "一键接入 OpenAI 兼容 API(CodeBuddy、vLLM、各类代理)并开启思考模式,绕开第三方端点以 content_filter 拒绝 developer 角色的问题。" + }, + "npm": "dsh-plugin-thinking-api", + "stars": 2, + "downloads": 352, + "install": "dsh plugin --profile web add dsh-plugin-thinking-api", + "added": "2026-08-19" + }, + { + "name": "dsh-swarm-router", + "owner": "r600a-code", + "url": "https://github.com/r600a-code/dsh-swarm-router", + "page": "https://awesome-dsh-plugin.com/p/r600a-code/dsh-swarm-router/", + "category": "model", + "description": { + "en": "Sub-agent matrix swarm that routes heterogeneous tasks to the most suitable model from an OpenRouter-like gateway plus cfgpu.com/llm/square, dispatches each via in-process subagents or direct LLM calls, and tracks per-model token consumption with a feedback-driven ranking.", + "zh": "子智能体矩阵蜂群:把异质任务路由到最合适的模型(OpenRouter 类网关 + cfgpu.com/llm/square),通过进程内子智能体或直接 LLM 调用下放,并按模型统计 token 消耗、用真实反馈驱动排名。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:r600a-code/dsh-swarm-router", + "added": "2026-08-16" + }, + { + "name": "dsh-subagent-model-picker", + "owner": "ringoage", + "url": "https://github.com/ringoage/dsh-subagent-model-picker", + "page": "https://awesome-dsh-plugin.com/p/ringoage/dsh-subagent-model-picker/", + "category": "model", + "description": { + "en": "A GUI subagent-model selector beside the main model seat: pick a model and reasoning effort per session, applied to every in-process subagent.", + "zh": "主模型选择器旁的子代理模型选择器:按会话选择模型与思考程度,应用到所有进程内子代理。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:ringoage/dsh-subagent-model-picker", + "added": "2026-08-16" + }, + { + "name": "dsh-codex-use#dsh-codex-appserver", + "owner": "seriousz158", + "url": "https://github.com/seriousz158/dsh-codex-use/tree/main/packages/dsh-codex-appserver", + "page": "https://awesome-dsh-plugin.com/p/seriousz158/dsh-codex-use--packages-dsh-codex-appserver/", + "category": "model", + "description": { + "en": "Adds OpenAI Codex (ChatGPT) as a selectable DSH provider via the local Codex App Server.", + "zh": "通过本机 Codex App Server,将 OpenAI Codex(ChatGPT)作为可选 DSH Provider。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:seriousz158/dsh-codex-use#path:/packages/dsh-codex-appserver", + "added": "2026-08-23" + }, + { + "name": "dsh-plugin-subagent-director", + "owner": "SeverusZh", + "url": "https://github.com/SeverusZh/dsh-plugin-subagent-director", + "page": "https://awesome-dsh-plugin.com/p/SeverusZh/dsh-plugin-subagent-director/", + "category": "model", + "description": { + "en": "Per-subagent LLM provider/model selection with role templates.", + "zh": "子代理 LLM 供应商/模型选择,支持角色模板。" + }, + "npm": "dsh-plugin-subagent-director", + "stars": 10, + "downloads": 795, + "install": "dsh plugin --profile web add dsh-plugin-subagent-director", + "added": "2026-08-17" + }, + { + "name": "dsh-model-management", + "owner": "sharewiner", + "url": "https://github.com/sharewiner/dsh-model-management", + "page": "https://awesome-dsh-plugin.com/p/sharewiner/dsh-model-management/", + "category": "model", + "description": { + "en": "Manages the default DSH model, provider and model visibility, and optional OpenAI Responses web search.", + "zh": "管理 DSH 默认模型、Provider 与模型可见性,并支持可选的 OpenAI Responses 联网搜索。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:sharewiner/dsh-model-management", + "added": "2026-08-27" + }, + { + "name": "dsh-codex-harness", + "owner": "shuind", + "url": "https://github.com/shuind/dsh-codex-harness", + "page": "https://awesome-dsh-plugin.com/p/shuind/dsh-codex-harness/", + "category": "model", + "description": { + "en": "Provides a Codex preset for DSH with Codex-style system instructions and tool contracts, image input, optional reasoning effort, Fast priority-tier requests, OpenAI Responses web search and remote compaction, and Web context-capacity settings; it is intended as a harness adapter for tokens originating from Codex and remains compatible with the DSH ecosystem.", + "zh": "在 DSH 中提供 Codex preset:引入 Codex 风格的系统提示词与工具契约,支持图片输入、可选思考强度、Fast 优先级请求、OpenAI Responses 网页搜索与远程压缩,以及在 Web 设置上下文容量;旨在给来源于Codex的token提供适配harness,并兼容享受DSH生态。" + }, + "npm": "@shuind/dsh-codex-harness", + "stars": 4, + "downloads": 1086, + "install": "dsh plugin --profile web add @shuind/dsh-codex-harness", + "added": "2026-08-23" + }, + { + "name": "dsh-role-router", + "owner": "SnowAmberX", + "url": "https://github.com/SnowAmberX/dsh-role-router", + "page": "https://awesome-dsh-plugin.com/p/SnowAmberX/dsh-role-router/", + "category": "model", + "description": { + "en": "Routes agent requests to configurable default, planner, and subagent models, automatically using the planner role in plan mode, with per-role reasoning effort and Web UI configuration.", + "zh": "按 default、planner 和 subagent 角色将代理请求路由到可配置模型,在计划模式下自动使用 planner,并提供逐角色推理强度与 Web UI 配置。" + }, + "npm": "@snowamberx/dsh-role-router", + "stars": 4, + "downloads": 751, + "install": "dsh plugin --profile web add @snowamberx/dsh-role-router", + "added": "2026-08-19" + }, + { + "name": "dsh-chatgpt-subscription", + "owner": "songoao25", + "url": "https://github.com/songoao25/dsh-chatgpt-subscription", + "page": "https://awesome-dsh-plugin.com/p/songoao25/dsh-chatgpt-subscription/", + "category": "model", + "description": { + "en": "Bind your ChatGPT account via official OAuth and chat with ChatGPT models inside DSH, using your Plus/Pro subscription quota.", + "zh": "通过官方 OAuth 绑定你的 ChatGPT 账号,在 DSH 内直接使用 ChatGPT 模型对话,消耗 Plus/Pro 订阅额度。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:songoao25/dsh-chatgpt-subscription", + "added": "2026-08-16" + }, + { + "name": "dsh-plugin-rollout-scout", + "owner": "SpookySandwich", + "url": "https://github.com/SpookySandwich/dsh-plugin-rollout-scout", + "page": "https://awesome-dsh-plugin.com/p/SpookySandwich/dsh-plugin-rollout-scout/", + "category": "model", + "description": { + "en": "Detects which conversation model an account is being served: launches concurrent throwaway probe conversations, scores each streaming chain-of-thought by how its paragraphs open, and cancels probes that read as the older model within seconds. Includes an offline self-check that replays 13 hand-labelled chains-of-thought through the same classifier.", + "zh": "检测账号当前被分配到哪个对话模型:并发发起一次性探测会话,按段落开头的写法为流式思维链打分,读起来像旧模型的在数秒内中止。附带离线自检,用同一分类器回放 13 条人工标注的思维链。" + }, + "npm": "dsh-plugin-rollout-scout", + "stars": 3, + "downloads": 315, + "install": "dsh plugin --profile web add dsh-plugin-rollout-scout", + "added": "2026-08-23" + }, + { + "name": "dsh-llm-qwen-local", + "owner": "starefinger", + "url": "https://github.com/starefinger/dsh-llm-qwen-local", + "page": "https://awesome-dsh-plugin.com/p/starefinger/dsh-llm-qwen-local/", + "category": "model", + "description": { + "en": "LLM adapter plugin for locally deployed Qwen models behind a vLLM OpenAI-compatible endpoint, with per-model multimodal switch, fully configurable reasoning efforts, and a web settings page for editing the deployment from the frontend.", + "zh": "面向 vLLM OpenAI 兼容端点上本地部署的 Qwen 模型的 LLM 适配器插件,支持按模型多模态开关、完全可配置的推理档位,以及可在前端编辑部署配置的 Web 设置页。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:starefinger/dsh-llm-qwen-local", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/starefinger/dsh-llm-qwen-local/HEAD/docs/assets/setting.png", + "https://raw.githubusercontent.com/starefinger/dsh-llm-qwen-local/HEAD/docs/assets/setting2.png", + "https://raw.githubusercontent.com/starefinger/dsh-llm-qwen-local/HEAD/docs/assets/use_guide_1.png", + "https://raw.githubusercontent.com/starefinger/dsh-llm-qwen-local/HEAD/docs/assets/use_guide_2.png" + ] + }, + { + "name": "dsh-codex-connect-plus", + "owner": "stoneface10", + "url": "https://github.com/stoneface10/dsh-codex-connect-plus", + "page": "https://awesome-dsh-plugin.com/p/stoneface10/dsh-codex-connect-plus/", + "category": "model", + "description": { + "en": "Adds ChatGPT/Codex OAuth-backed Codex models and gpt-image-2 generation/editing to DSH without requiring an OpenAI Platform API key.", + "zh": "为 DSH 添加基于 ChatGPT/Codex OAuth 的 Codex 模型及 gpt-image-2 文生图和编辑能力,无需 OpenAI Platform API Key。" + }, + "npm": "dsh-codex-connect-plus", + "stars": 4, + "downloads": 771, + "install": "dsh plugin --profile web add dsh-codex-connect-plus", + "added": "2026-08-17" + }, + { + "name": "dsh-AuthInOne", + "owner": "Stormycry-cryp", + "url": "https://github.com/Stormycry-cryp/dsh-AuthInOne", + "page": "https://awesome-dsh-plugin.com/p/Stormycry-cryp/dsh-AuthInOne/", + "category": "model", + "description": { + "en": "Adds account login, API and custom Provider setup, model switching, image fallback for text-only models, and token/cost attribution to DeepSeek Harness 47f.", + "zh": "为 DeepSeek Harness 47f 提供账号登录、API 与自定义 Provider 配置、模型切换、纯文本模型图片兜底,以及 Token 与费用归因。" + }, + "npm": null, + "tarball": "https://github.com/Stormycry-cryp/dsh-AuthInOne/releases/download/v0.2.0-alpha.4/dsh-auth-in-one-0.2.0-alpha.4.tgz", + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/Stormycry-cryp/dsh-AuthInOne/releases/download/v0.2.0-alpha.4/dsh-auth-in-one-0.2.0-alpha.4.tgz\"", + "added": "2026-08-27" + }, + { + "name": "dsh-codex-auth", + "owner": "suntianc", + "url": "https://github.com/suntianc/dsh-codex-auth", + "page": "https://awesome-dsh-plugin.com/p/suntianc/dsh-codex-auth/", + "category": "model", + "description": { + "en": "Reuses the Codex CLI ChatGPT login as an `openai-codex` LLM route and adds GPT Auth controls to DSH Web settings.", + "zh": "复用 Codex CLI 的 ChatGPT 登录态注册 `openai-codex` LLM 路由,并在 DSH Web 设置中提供 GPT Auth 控件。" + }, + "npm": "dsh-codex-auth", + "stars": 13, + "downloads": 1445, + "install": "dsh plugin --profile web add dsh-codex-auth", + "added": "2026-08-14" + }, + { + "name": "dsh-model-router", + "owner": "superboy911", + "url": "https://github.com/superboy911/dsh-model-router", + "page": "https://awesome-dsh-plugin.com/p/superboy911/dsh-model-router/", + "category": "model", + "description": { + "en": "Deterministic keyword routing, allowlisted model switching, and an isolated image_gen channel for DeepSeek Harness.", + "zh": "DSH 关键词路由与隔离生图插件:确定性关键词路由、白名单模型切换与隔离的 image_gen 生图通道。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:superboy911/dsh-model-router", + "added": "2026-08-14" + }, + { + "name": "dsh-llm-proxy", + "owner": "superfish058", + "url": "https://github.com/superfish058/dsh-llm-proxy", + "page": "https://awesome-dsh-plugin.com/p/superfish058/dsh-llm-proxy/", + "category": "model", + "description": { + "en": "Per-model proxy routing for LLM requests: selected models go through a proxy (Clash etc.) while the rest stay direct, with automatic retry on transport errors, 429 and 5xx, configured live from the settings page.", + "zh": "按模型给 LLM 请求分流代理:勾选的模型走代理(Clash 等),其余直连;传输错误、429、5xx 自动重试,设置页实时生效。" + }, + "npm": "@superfish058/dsh-llm-proxy", + "stars": 4, + "downloads": 1292, + "install": "dsh plugin --profile web add @superfish058/dsh-llm-proxy", + "added": "2026-08-19" + }, + { + "name": "dsh-model-reasoning", + "owner": "TikaFlow", + "url": "https://github.com/TikaFlow/dsh-model-reasoning", + "page": "https://awesome-dsh-plugin.com/p/TikaFlow/dsh-model-reasoning/", + "category": "model", + "description": { + "en": "Automatically fills in reasoning-effort levels for models from unofficial (custom) providers, with data sourced from models.dev.", + "zh": "为所有非官方(自定义)提供商的模型自动填充推理级别,数据来自 models.dev。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:TikaFlow/dsh-model-reasoning", + "added": "2026-08-18" + }, + { + "name": "dsh-ccswitch", + "owner": "upJiang", + "url": "https://github.com/upJiang/dsh-ccswitch", + "page": "https://awesome-dsh-plugin.com/p/upJiang/dsh-ccswitch/", + "category": "model", + "description": { + "en": "Adds CC Switch providers and models to DeepSeek Harness, with provider filtering and model-name search.", + "zh": "将 CC Switch 中的 provider 和模型接入 DeepSeek Harness,并支持 provider 筛选和模型名称搜索。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:upJiang/dsh-ccswitch", + "added": "2026-08-21" + }, + { + "name": "dsh-plugin-subscriptions", + "owner": "V1ki", + "url": "https://github.com/V1ki/dsh-plugin-subscriptions", + "page": "https://awesome-dsh-plugin.com/p/V1ki/dsh-plugin-subscriptions/", + "category": "model", + "description": { + "en": "Use ChatGPT (Codex), Claude, and Grok subscriptions as DeepSeek Harness LLM providers, with Settings login, model catalogs, usage, plus image_generate, video_generate, and x_search tools.", + "zh": "把 ChatGPT(Codex)、Claude、Grok 订阅当作 DeepSeek Harness 的 LLM 提供方:设置页登录、模型目录、用量展示,以及 image_generate、video_generate 与 x_search 工具。" + }, + "npm": "dsh-plugin-subscriptions", + "stars": 274, + "downloads": 4023, + "install": "dsh plugin --profile web add dsh-plugin-subscriptions", + "added": "2026-08-21", + "screenshots": [ + "https://raw.githubusercontent.com/V1ki/dsh-plugin-subscriptions/HEAD/docs/images/subscriptions.png", + "https://raw.githubusercontent.com/V1ki/dsh-plugin-subscriptions/HEAD/docs/images/model-picker.png", + "https://raw.githubusercontent.com/V1ki/dsh-plugin-subscriptions/HEAD/docs/images/model-effort.png", + "https://raw.githubusercontent.com/V1ki/dsh-plugin-subscriptions/HEAD/docs/images/speed-toggle.png", + "https://raw.githubusercontent.com/V1ki/dsh-plugin-subscriptions/HEAD/docs/images/image-generate-inline.png", + "https://raw.githubusercontent.com/V1ki/dsh-plugin-subscriptions/HEAD/docs/images/image-generate-providers.png", + "https://raw.githubusercontent.com/V1ki/dsh-plugin-subscriptions/HEAD/docs/images/video-generate-inline.png" + ] + }, + { + "name": "dsh-delegation-suite", + "owner": "wenheguo2", + "url": "https://github.com/wenheguo2/dsh-delegation-suite", + "page": "https://awesome-dsh-plugin.com/p/wenheguo2/dsh-delegation-suite/", + "category": "model", + "description": { + "en": "Ranked subagent delegation: role-based model routing with failover, per-route reasoning effort, model-transparent labels, fork delegation, and a visual route editor.", + "zh": "智能委派套件:按角色路由最强模型、失败自动互备、每个子代理可设思考强度、名字带模型标签、支持 fork 委派与可视化路由编辑。" + }, + "npm": null, + "tarball": "https://github.com/wenheguo2/dsh-delegation-suite/releases/download/v1.0.0/dsh-delegation-suite-1.0.0.tgz", + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/wenheguo2/dsh-delegation-suite/releases/download/v1.0.0/dsh-delegation-suite-1.0.0.tgz\"", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/wenheguo2/dsh-delegation-suite/main/assets/routes.png", + "https://raw.githubusercontent.com/wenheguo2/dsh-delegation-suite/main/assets/subagents.png" + ] + }, + { + "name": "dsh-llm-newapi", + "owner": "wenzetan", + "url": "https://github.com/wenzetan/dsh-llm-newapi", + "page": "https://awesome-dsh-plugin.com/p/wenzetan/dsh-llm-newapi/", + "category": "model", + "description": { + "en": "NewAPI (OpenAI-compatible gateway) LLM provider: registers a `newapi` route with chat-only model discovery, auto-fills model parameters (context window, reasoning effort) from models.dev, and adds a Web settings section for the base URL and API key.", + "zh": "NewAPI(OpenAI 兼容网关)模型接入:注册 `newapi` 路由,仅发现聊天类模型,自动从 models.dev 获取模型参数(上下文窗口、思考强度等)并填充,并在 Web 设置页配置 base URL 与 API Key。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:wenzetan/dsh-llm-newapi", + "added": "2026-08-15" + }, + { + "name": "dsh-model-capabilities", + "owner": "WJZ-P", + "url": "https://github.com/WJZ-P/dsh-model-capabilities", + "page": "https://awesome-dsh-plugin.com/p/WJZ-P/dsh-model-capabilities/", + "category": "model", + "description": { + "en": "Adds an input-modality selector to custom model settings in the DeepSeek Harness Web UI.", + "zh": "为 DeepSeek Harness Web UI 的自定义模型设置添加输入模态选择器。" + }, + "npm": "dsh-model-capability", + "stars": 3, + "downloads": 291, + "install": "dsh plugin --profile web add dsh-model-capability", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/WJZ-P/dsh-model-capabilities/main/assets/screenshots/01-model-input-selector.png" + ] + }, + { + "name": "dsh-codex-oauth", + "owner": "WNJXYK", + "url": "https://github.com/WNJXYK/dsh-codex-oauth", + "page": "https://awesome-dsh-plugin.com/p/WNJXYK/dsh-codex-oauth/", + "category": "model", + "description": { + "en": "Use a ChatGPT/Codex subscription in DeepSeek Harness with GPT models, image generation, web search, subscription quota reporting, model and feature controls, and browser or device-code OAuth sign-in.", + "zh": "在 DeepSeek Harness 中使用 ChatGPT/Codex 订阅接入 GPT 模型、图像生成与 Web 搜索,并支持订阅额度显示、模型与功能控制,以及浏览器或设备码 OAuth 登录。" + }, + "npm": "@wnjxyk/dsh-codex-oauth", + "stars": 15, + "downloads": 781, + "install": "dsh plugin --profile web add @wnjxyk/dsh-codex-oauth", + "added": "2026-08-16" + }, + { + "name": "dsh-model-pro", + "owner": "wqy8593521", + "url": "https://github.com/wqy8593521/dsh-model-pro", + "page": "https://awesome-dsh-plugin.com/p/wqy8593521/dsh-model-pro/", + "category": "model", + "description": { + "en": "Lifecycle management UI for the llm-pi-ai model provider: create, edit, delete, enable and disable providers with custom headers and AES-GCM-encrypted API keys; uninstall-safe restore of parked providers, remote model discovery with batch write, smart routing with five strategies, union/intersection composites, per-model connectivity tests, and request observability.", + "zh": "llm-pi-ai 模型提供商的生命周期管理 UI:提供商的新建/编辑/删除与启停(自定义请求头、AES-GCM 加密密钥);卸载时安全还原滞留提供商,远端模型发现与批量写入,五种策略的智能路由,并集/交集组合提供商,逐模型连通性测试与请求观测。" + }, + "npm": "dsh-model-pro", + "stars": 4, + "downloads": 1616, + "install": "dsh plugin --profile web add dsh-model-pro", + "added": "2026-08-22", + "screenshots": [ + "https://raw.githubusercontent.com/wqy8593521/dsh-model-pro/main/docs/screenshots/dashboard.png", + "https://raw.githubusercontent.com/wqy8593521/dsh-model-pro/main/docs/screenshots/editor.png", + "https://raw.githubusercontent.com/wqy8593521/dsh-model-pro/main/docs/screenshots/models.png", + "https://raw.githubusercontent.com/wqy8593521/dsh-model-pro/main/docs/screenshots/test.png", + "https://raw.githubusercontent.com/wqy8593521/dsh-model-pro/main/docs/screenshots/routes.png" + ] + }, + { + "name": "dsh-codex-subscription", + "owner": "WSL043", + "url": "https://github.com/WSL043/dsh-codex-subscription", + "page": "https://awesome-dsh-plugin.com/p/WSL043/dsh-codex-subscription/", + "category": "model", + "description": { + "en": "Built-in ChatGPT OAuth provider for Codex models, selectable subscription web search, backend quota for standard Codex and Spark, and a DSH settings UI; no API key or separate Codex installation required.", + "zh": "内置 ChatGPT OAuth 的 Codex 模型提供方:可切换订阅联网搜索,在设置页显示普通 Codex 与 Spark 独立额度;无需 API Key,也不需要另装 Codex。" + }, + "npm": "dsh-codex-subscription", + "stars": 23, + "downloads": 6209, + "install": "dsh plugin --profile web add dsh-codex-subscription", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/WSL043/dsh-codex-subscription/main/docs/assets/settings-en.png", + "https://raw.githubusercontent.com/WSL043/dsh-codex-subscription/main/docs/assets/composer-quota-en.png" + ] + }, + { + "name": "dsh-plugin-codex", + "owner": "wss534857356", + "url": "https://github.com/wss534857356/dsh-plugin-codex", + "page": "https://awesome-dsh-plugin.com/p/wss534857356/dsh-plugin-codex/", + "category": "model", + "description": { + "en": "Codex App Server model provider using a local Codex login, with session reuse, Harness tool bridging, native action traces, and durable generated-image projection.", + "zh": "使用本地 Codex 登录的 Codex App Server 模型提供方,支持会话复用、Harness 工具桥接、原生动作轨迹和生成图片持久化。" + }, + "npm": "dsh-llm-codex-app-server", + "stars": 5, + "downloads": 141, + "install": "dsh plugin --profile web add dsh-llm-codex-app-server", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/wss534857356/dsh-plugin-codex/main/docs/images/conversation-screenshot.png", + "https://raw.githubusercontent.com/wss534857356/dsh-plugin-codex/main/docs/images/model-selector-screenshot.png" + ] + }, + { + "name": "dsh-opencode-models", + "owner": "wyouwd1", + "url": "https://github.com/wyouwd1/dsh-opencode-models", + "page": "https://awesome-dsh-plugin.com/p/wyouwd1/dsh-opencode-models/", + "category": "model", + "description": { + "en": "Manages OpenCode Zen free-tier and Go-tier models in settings.yaml — fetches the live model listings from opencode.ai, reports configured-versus-live drift per route, and adds or removes entries through four agent tools and an OpenCode Models settings section.", + "zh": "在 settings.yaml 中管理 OpenCode Zen 免费档与 Go 档模型——实时拉取 opencode.ai 的最新模型列表,按路由对比已配置与线上差异,通过四个 agent 工具和设置页分区增删模型条目。" + }, + "npm": null, + "tarball": "https://github.com/wyouwd1/dsh-opencode-models/releases/latest/download/dsh-opencode-models.tgz", + "stars": 0, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/wyouwd1/dsh-opencode-models/releases/latest/download/dsh-opencode-models.tgz\"", + "added": "2026-08-25" + }, + { + "name": "dsh-opencode-zen", + "owner": "xiaozhe7772222", + "url": "https://github.com/xiaozhe7772222/dsh-opencode-zen", + "page": "https://awesome-dsh-plugin.com/p/xiaozhe7772222/dsh-opencode-zen/", + "category": "model", + "description": { + "en": "Zero-cost access to 6 free large models. No registration, no recharge, 6 free models built-in, multi-key rotation and rate-limit backoff.", + "zh": "0元接入6个免费大模型,免注册免充值,内置6个免费模型,多Key轮换与限流退避。" + }, + "npm": null, + "stars": 15, + "downloads": null, + "install": "dsh plugin --profile web add github:xiaozhe7772222/dsh-opencode-zen", + "added": "2026-08-21" + }, + { + "name": "dsh-grok-provider", + "owner": "yoshino-xiao7", + "url": "https://github.com/yoshino-xiao7/dsh-grok-provider", + "page": "https://awesome-dsh-plugin.com/p/yoshino-xiao7/dsh-grok-provider/", + "category": "model", + "description": { + "en": "Grok Build provider using the official CLI browser login, with dynamic account-visible models, streaming reasoning and tools, plus quota and model-capability settings.", + "zh": "Grok Build 模型接入插件:使用官方 CLI 浏览器登录,动态发现账号可见模型,支持流式推理、工具调用、额度与模型能力设置页。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:yoshino-xiao7/dsh-grok-provider", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/yoshino-xiao7/dsh-grok-provider/HEAD/.github/assets/plugin-preview/account-dashboard.png", + "https://raw.githubusercontent.com/yoshino-xiao7/dsh-grok-provider/HEAD/.github/assets/plugin-preview/model-picker.png", + "https://raw.githubusercontent.com/yoshino-xiao7/dsh-grok-provider/HEAD/.github/assets/plugin-preview/grok-context.png" + ] + }, + { + "name": "dsh-codex-switch", + "owner": "ZChenW", + "url": "https://github.com/ZChenW/dsh-codex-switch", + "page": "https://awesome-dsh-plugin.com/p/ZChenW/dsh-codex-switch/", + "category": "model", + "description": { + "en": "Connects ChatGPT accounts through OAuth, switches among them manually or automatically, and adds Codex models with usage limits to DeepSeek Harness.", + "zh": "通过 OAuth 连接 ChatGPT 账号,支持手动或自动切换,并为 DeepSeek Harness 添加带用量限额信息的 Codex 模型。" + }, + "npm": "dsh-codex-switch", + "stars": 1, + "downloads": 789, + "install": "dsh plugin --profile web add dsh-codex-switch", + "added": "2026-08-23" + }, + { + "name": "evo-subagent", + "owner": "ZekaiShi", + "url": "https://github.com/ZekaiShi/evo-subagent", + "page": "https://awesome-dsh-plugin.com/p/ZekaiShi/evo-subagent/", + "category": "model", + "description": { + "en": "Maps a stable agent_key to an exact provider/model pair declared in role Markdown front matter, validates the pair against the live DSH model registry before spawning, and maintains per-workspace prefercmd and memory evolution files.", + "zh": "将稳定的 agent_key 映射到角色 Markdown 文件 front matter 中声明的具体 provider/model 组合,spawn 前会对照 DSH 模型注册表校验该组合,并按工作区维护 prefercmd 与 memory 进化文件。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:ZekaiShi/evo-subagent", + "added": "2026-08-26" + }, + { + "name": "dsh-llm-retry-settings", + "owner": "zeng6125-rgb", + "url": "https://github.com/zeng6125-rgb/dsh-llm-retry-settings", + "page": "https://awesome-dsh-plugin.com/p/zeng6125-rgb/dsh-llm-retry-settings/", + "category": "model", + "description": { + "en": "Settings card for the DSH LLM auto-retry engine: tune retry count, backoff and jitter live from Settings -> General, with configurable retryableCodes chips - defaults add INVALID_REQUEST so OpenAI-style HTTP 400 reasoning_text errors in thinking mode get retried automatically.", + "zh": "LLM 自动重试设置卡片:在设置页实时调整重试次数、退避与抖动,并可勾选额外可重试错误码(retryableCodes)—— 默认补入 INVALID_REQUEST,OpenAI thinking 模式 HTTP 400 reasoning_text 报错开箱即自动重试。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:zeng6125-rgb/dsh-llm-retry-settings", + "added": "2026-08-19" + }, + { + "name": "dsh-claude-driver", + "owner": "zhangjunjesse", + "url": "https://github.com/zhangjunjesse/dsh-claude-driver", + "page": "https://awesome-dsh-plugin.com/p/zhangjunjesse/dsh-claude-driver/", + "category": "model", + "description": { + "en": "Run the main session model on a local Claude Code subscription through the official Claude Agent SDK, taking over the llm/stream route for the claude-code provider, with token-level streaming, session resume, and DSH tools bridged in over MCP so they keep DSH sandbox and approval.", + "zh": "让会话主模型跑在本机 Claude Code 订阅上:经官方 Claude Agent SDK 接管 claude-code provider 的 llm/stream 路由,支持 token 级流式、会话 resume 续接,并把 DSH 工具经 MCP 桥接进去以保留 DSH 的沙箱与审批。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:zhangjunjesse/dsh-claude-driver", + "added": "2026-08-27" + }, + { + "name": "dsh-awiki", + "owner": "AgentConnect", + "url": "https://github.com/AgentConnect/dsh-awiki", + "page": "https://awesome-dsh-plugin.com/p/AgentConnect/dsh-awiki/", + "category": "identity", + "description": { + "en": "Provides DeepSeek Harness agents with native identities based on the open Agent Network Protocol (ANP), plus identity-based direct, group, mail, and Agent-to-Agent communication.", + "zh": "为 DeepSeek Harness 智能体提供基于开放协议ANP的原生身份,以及基于该身份的私聊、群聊、邮件和智能体间通信能力。" + }, + "npm": "@awiki/dsh-plugin", + "stars": 12, + "downloads": 2521, + "install": "dsh plugin --profile web add @awiki/dsh-plugin", + "added": "2026-08-20" + }, + { + "name": "dsh-auth-everying", + "owner": "chenbin-dev", + "url": "https://github.com/chenbin-dev/dsh-auth-everying", + "page": "https://awesome-dsh-plugin.com/p/chenbin-dev/dsh-auth-everying/", + "category": "identity", + "description": { + "en": "Import local Claude, Codex, Grok, Gemini, Copilot, OpenCode, and CC Switch config into DeepSeek Harness with OAuth login for supported providers.", + "zh": "Import local Claude, Codex, Grok, Gemini, Copilot, OpenCode, and CC Switch config into DeepSeek Harness with OAuth login for supported providers." + }, + "npm": "dsh-auth-everying", + "stars": 3, + "downloads": 159, + "install": "dsh plugin --profile web add dsh-auth-everying", + "added": "2026-08-23" + }, + { + "name": "dsh-ponytail", + "owner": "MengYuil", + "url": "https://github.com/MengYuil/dsh-ponytail", + "page": "https://awesome-dsh-plugin.com/p/MengYuil/dsh-ponytail/", + "category": "identity", + "description": { + "en": "Port of ponytail: an always-on lazy-senior-developer coding persona with intensity levels, persistence of the default level, and review, audit, debt, gain and help skills for DeepSeek Harness.", + "zh": "ponytail 移植:常驻的「懒惰资深开发者」编码人设,带强度档位与 review、audit、debt、gain、help 技能,适用于 DeepSeek Harness。" + }, + "npm": "@mengyuly/dsh-ponytail", + "tarball": "https://github.com/MengYuil/dsh-ponytail/releases/latest/download/mengyuly-dsh-ponytail-0.2.1.tgz", + "stars": 2, + "downloads": 660, + "install": "dsh plugin --profile web add @mengyuly/dsh-ponytail", + "added": "2026-08-24" + }, + { + "name": "dsh-remote-shell", + "owner": "wutian122", + "url": "https://github.com/wutian122/dsh-remote-shell", + "page": "https://awesome-dsh-plugin.com/p/wutian122/dsh-remote-shell/", + "category": "identity", + "description": { + "en": "Bundles the remote-shell skill for secure SSH, SFTP, Telnet, and WinRM remote operations, with an encrypted credential vault whose login and execution scripts never accept plaintext passwords.", + "zh": "打包 remote-shell 技能,提供安全的 SSH、SFTP、Telnet、WinRM 远程操作,内置加密凭证库,登录与执行脚本不接受明文密码。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:wutian122/dsh-remote-shell", + "added": "2026-08-27" + }, + { + "name": "dsh-undo-plugin", + "owner": "23swccp", + "url": "https://github.com/23swccp/dsh-undo/tree/master/packages/bundle-rollback", + "page": "https://awesome-dsh-plugin.com/p/23swccp/dsh-undo--packages-bundle-rollback/", + "category": "session", + "description": { + "en": "Conversation and workspace undo: use the /undo timeline, session-header action, or message icon to roll back before any sent prompt; private Shadow Git snapshots restore files, the reverted turns stay out of model context, and Undo Rollback can recover mistakes. Includes archived-session management and per-tool colored cards.", + "zh": "对话与工作区撤销:通过 /undo 节点轴、会话头部按钮或消息下方图标回滚到任意已发送 prompt 之前;插件私有 Shadow Git 快照恢复文件,被回滚回合不会进入模型上下文,误操作还能“撤回回滚”。附带归档会话管理与工具卡片分类配色。" + }, + "npm": "dsh-undo-plugin", + "stars": 2, + "downloads": 457, + "install": "dsh plugin --profile web add dsh-undo-plugin", + "added": "2026-08-21" + }, + { + "name": "dsh-inline-images", + "owner": "3403473060", + "url": "https://github.com/3403473060/dsh-inline-images", + "page": "https://awesome-dsh-plugin.com/p/3403473060/dsh-inline-images/", + "category": "session", + "description": { + "en": "Render local image paths from assistant replies inline in the message body (9 formats, click-to-zoom lightbox, adjustable size).", + "zh": "对话内联图片:LLM 回复中输出的本地图片路径在消息正文直接渲染为图片(9 种格式、点击放大灯箱、可调尺寸)。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:3403473060/dsh-inline-images", + "added": "2026-08-14" + }, + { + "name": "dsh-archived-conversations", + "owner": "AKS1st", + "url": "https://github.com/AKS1st/dsh-archived-conversations", + "page": "https://awesome-dsh-plugin.com/p/AKS1st/dsh-archived-conversations/", + "category": "session", + "description": { + "en": "Archived conversations list in the sidebar footer with read-only previews of the most recent messages, for sessions the product deliberately keeps hidden and unreopenable.", + "zh": "侧边栏底部的已归档对话列表,可只读预览最近消息;针对产品刻意隐藏且无法重新打开的归档会话。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:AKS1st/dsh-archived-conversations", + "added": "2026-08-15" + }, + { + "name": "dsh-turn-rewind", + "owner": "Anionex", + "url": "https://github.com/Anionex/dsh-turn-rewind", + "page": "https://awesome-dsh-plugin.com/p/Anionex/dsh-turn-rewind/", + "category": "session", + "description": { + "en": "Rewind conversation and workspace state, powered by a persistent Change Ledger.", + "zh": "对话回退:基于持久 Change Ledger 回滚会话与工作区状态。" + }, + "npm": "@anionex/dsh-turn-rewind", + "stars": 100, + "downloads": 4553, + "install": "dsh plugin --profile web add @anionex/dsh-turn-rewind", + "added": "2026-08-13" + }, + { + "name": "dsh-context-console", + "owner": "anweat", + "url": "https://github.com/anweat/dsh-context-console", + "page": "https://awesome-dsh-plugin.com/p/anweat/dsh-context-console/", + "category": "session", + "description": { + "en": "Inspect and simulate DSH context consumption with trajectory and inventory views, and create, inject, import, or safely repair session messages through the integrated Message Forge.", + "zh": "上下文控制台:通过轨迹墙与库存视图检查并模拟 DSH 上下文消费,并由内置消息锻造台创建、注入、导入或安全修复会话消息。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:anweat/dsh-context-console", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/anweat/dsh-context-console/main/docs/images/context-console-trajectory.png", + "https://raw.githubusercontent.com/anweat/dsh-context-console/main/docs/images/context-console-inventory.png", + "https://raw.githubusercontent.com/anweat/dsh-context-console/main/docs/images/context-console-forge.png" + ] + }, + { + "name": "dsh-side-chat-plus-plus", + "owner": "anzhaohao", + "url": "https://github.com/anzhaohao/dsh-side-chat-plus-plus", + "page": "https://awesome-dsh-plugin.com/p/anzhaohao/dsh-side-chat-plus-plus/", + "category": "session", + "description": { + "en": "Multi-tab side chat for DeepSeek Harness. Quote a whole message or text selected from a still-streaming reply into a side chat without waiting for the reply to finish, and dock the panel over the better-sidebar workbench or yield to the conversation depending on the sidebar state.", + "zh": "DeepSeek Harness 多标签侧聊增强:可引用整条消息,也可在回复生成中选中文本追问(无需等待回复完成);右侧停靠面板随 better-sidebar 开关在覆盖与让位间自动切换。" + }, + "npm": "dsh-side-chat-plus-plus", + "stars": 0, + "downloads": 301, + "install": "dsh plugin --profile web add dsh-side-chat-plus-plus", + "added": "2026-08-25" + }, + { + "name": "dsh-message-copy-enhance", + "owner": "Asianfleet", + "url": "https://github.com/Asianfleet/dsh-message-copy-enhance", + "page": "https://awesome-dsh-plugin.com/p/Asianfleet/dsh-message-copy-enhance/", + "category": "session", + "description": { + "en": "Copy selections from assistant messages as Markdown, preserving links, LaTeX sources, and code-fence language info.", + "zh": "将助手消息中选中的内容复制为 Markdown,保留链接、LaTeX 源码与代码块语言信息。" + }, + "npm": "dsh-message-copy-enhance", + "stars": 1, + "downloads": 662, + "install": "dsh plugin --profile web add dsh-message-copy-enhance", + "added": "2026-08-19" + }, + { + "name": "huahua-dsh-record-replay", + "owner": "azure5100", + "url": "https://github.com/azure5100/huahua-dsh-record-replay", + "page": "https://awesome-dsh-plugin.com/p/azure5100/huahua-dsh-record-replay/", + "category": "session", + "description": { + "en": "Full-featured record & replay for DSH (works on Windows) from the huahua plugin series: replay every auto-recorded session as a readable timeline, export/import shareable replay packs, re-run conversations in a fresh session, and record screen operations to auto-generate installable SKILL.md skills. Includes a 4-file Windows adaptation of kangshifu1/dsh-record-replay for DSH rc.5 (see README derivative notice).", + "zh": "功能完整的 DSH 录制回放插件(huahua 出品,Windows 实测可用):会话时间线回放、回放包导出/导入、复刻会话重新执行、录屏自动生成可安装技能。基于 kangshifu1/dsh-record-replay 做 4 处 Windows/rc.5 适配(见 README 衍生声明)。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:azure5100/huahua-dsh-record-replay", + "added": "2026-08-20" + }, + { + "name": "dsh-session-mover", + "owner": "baisama-cloud", + "url": "https://github.com/baisama-cloud/dsh-session-mover", + "page": "https://awesome-dsh-plugin.com/p/baisama-cloud/dsh-session-mover/", + "category": "session", + "description": { + "en": "Move a session to another workspace by dragging it onto the target workspace header in the sidebar: the session is cloned with full history into the target workspace and the original is archived.", + "zh": "在侧边栏把会话直接拖到其他工作区的标题行即可移动:目标工作区生成携带完整历史的新会话,原会话归档。" + }, + "npm": null, + "tarball": "https://github.com/baisama-cloud/dsh-session-mover/releases/download/v0.1.0/dsh-session-mover-0.1.0.tgz", + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/baisama-cloud/dsh-session-mover/releases/download/v0.1.0/dsh-session-mover-0.1.0.tgz\"", + "added": "2026-08-18" + }, + { + "name": "dsh-bridge", + "owner": "baixianger", + "url": "https://github.com/baixianger/dsh-bridge", + "page": "https://awesome-dsh-plugin.com/p/baixianger/dsh-bridge/", + "category": "session", + "description": { + "en": "Local cross-session messaging for DSH agents, with discovery, cold-session wake-up delivery, and auditable message history.", + "zh": "面向 DSH Agent 的本地跨会话消息桥:支持会话发现、冷会话唤醒投递与可审计消息记录。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:baixianger/dsh-bridge", + "added": "2026-08-27" + }, + { + "name": "dsh-chat", + "owner": "baixianger", + "url": "https://github.com/baixianger/dsh-chat", + "page": "https://awesome-dsh-plugin.com/p/baixianger/dsh-chat/", + "category": "session", + "description": { + "en": "Group chat rooms for DSH sessions with explicit @mentions, cold-session wake-up delivery, and optional cross-host transport through dsh-weave.", + "zh": "面向 DSH 会话的群聊室:支持明确 @ 提及、冷会话唤醒投递,并可通过 dsh-weave 跨主机通信。" + }, + "npm": "dsh-chat", + "stars": 1, + "downloads": 3157, + "install": "dsh plugin --profile web add dsh-chat", + "added": "2026-08-19" + }, + { + "name": "dsh-tidychat", + "owner": "BananaSoldier01", + "url": "https://github.com/BananaSoldier01/dsh-tidychat", + "page": "https://awesome-dsh-plugin.com/p/BananaSoldier01/dsh-tidychat/", + "category": "session", + "description": { + "en": "Fold completed turns down to their final conclusions, insert a divider line between thinking and text, and an adaptive left-edge navigation rail with hover summaries and click-to-jump; optional idle-time loading of earlier history and one-click diagnostic report to GitHub issues.", + "zh": "已完成轮次自动折叠只留最终结论、思考与正文之间插入分隔线、自适应左缘定位条(悬停摘要 + 点击跳转);可选空闲时自动加载更早历史,异常时可一键生成诊断报告提交 GitHub issue。" + }, + "npm": "@bananasoldier01/dsh-tidychat", + "stars": 6, + "downloads": 74, + "install": "dsh plugin --profile web add @bananasoldier01/dsh-tidychat", + "added": "2026-08-24", + "screenshots": [ + "https://raw.githubusercontent.com/BananaSoldier01/dsh-tidychat/HEAD/assets/navigator.png", + "https://raw.githubusercontent.com/BananaSoldier01/dsh-tidychat/HEAD/assets/settings.png" + ] + }, + { + "name": "dsh-companion", + "owner": "beijingwahw", + "url": "https://github.com/beijingwahw/dsh-companion", + "page": "https://awesome-dsh-plugin.com/p/beijingwahw/dsh-companion/", + "category": "session", + "description": { + "en": "Four-module session companion: smart conversation export (Markdown/PDF/JSON/PNG long-image with privacy redaction and batch ZIP), context handoff summaries with template save/import, API cost optimization (live official pricing, peak/off-peak scheduling, daily/monthly budgets, model routing), and global conversation search with in-chat find (Ctrl+F, CSS Custom Highlight API).", + "zh": "四模块会话伴侣:对话智能导出(Markdown/PDF/JSON/PNG 长图、隐私脱敏、批量 ZIP)、上下文交接摘要(模板保存与导入继承)、API 成本优化(官方动态计价、峰谷调度、日/月双档预算、模型路由)、全局对话检索与对话内搜索(Ctrl+F、CSS Custom Highlight API)。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:beijingwahw/dsh-companion", + "added": "2026-08-15" + }, + { + "name": "dsh-conversation-share", + "owner": "bill9109", + "url": "https://github.com/bill9109/dsh-conversation-share", + "page": "https://awesome-dsh-plugin.com/p/bill9109/dsh-conversation-share/", + "category": "session", + "description": { + "en": "Share any excerpt of a conversation.", + "zh": "分享任意段落的对话。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:bill9109/dsh-conversation-share", + "added": "2026-08-13" + }, + { + "name": "dsh-enter-customizer", + "owner": "Boliban", + "url": "https://github.com/Boliban/dsh-enter-customizer", + "page": "https://awesome-dsh-plugin.com/p/Boliban/dsh-enter-customizer/", + "category": "session", + "description": { + "en": "Take over the system input shortcuts for the chat input box and configure behavior independently for each shortcut.", + "zh": "接管聊天输入框的回车等快捷键,每个快捷键的行为都能单独配置。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:Boliban/dsh-enter-customizer", + "added": "2026-08-15" + }, + { + "name": "dsh-sidechain", + "owner": "Buyi-wsgzg", + "url": "https://github.com/Buyi-wsgzg/dsh-sidechain", + "page": "https://awesome-dsh-plugin.com/p/Buyi-wsgzg/dsh-sidechain/", + "category": "session", + "description": { + "en": "`/side` persistent side sessions and `/btw` one-shot side questions, run in a temporary fork without touching main history.", + "zh": "`/side` 持续性侧会话与 `/btw` 一次性侧问,在临时 fork 中运行、不写入主会话历史。" + }, + "npm": null, + "stars": 10, + "downloads": null, + "install": "dsh plugin --profile web add github:Buyi-wsgzg/dsh-sidechain", + "added": "2026-08-13" + }, + { + "name": "dsh-session-export", + "owner": "bwndlct", + "url": "https://github.com/bwndlct/dsh-session-export", + "page": "https://awesome-dsh-plugin.com/p/bwndlct/dsh-session-export/", + "category": "session", + "description": { + "en": "Export the current session to portable, schema-versioned Markdown and JSON files via the `session_export` tool and slash commands, with cross-platform-safe filenames.", + "zh": "把当前会话导出为可移植、带 schema 版本的 Markdown 与 JSON 文件,提供 `session_export` 工具与斜杠命令两种入口,文件名跨平台安全。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:bwndlct/dsh-session-export", + "added": "2026-08-14" + }, + { + "name": "organize-workspace-sessions", + "owner": "caoqinnan-web", + "url": "https://github.com/caoqinnan-web/organize-workspace-sessions", + "page": "https://awesome-dsh-plugin.com/p/caoqinnan-web/organize-workspace-sessions/", + "category": "session", + "description": { + "en": "Organize DeepSeek Harness workspace sessions by renaming them as 类别|主题 and reporting archive, rename, and review suggestions.", + "zh": "整理 DeepSeek Harness 工作区会话,按 类别|主题 重命名并给出归档、改名与待判断建议。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:caoqinnan-web/organize-workspace-sessions", + "added": "2026-08-17" + }, + { + "name": "bites-the-dsh", + "owner": "CH4ACKO3", + "url": "https://github.com/CH4ACKO3/bites-the-dsh", + "page": "https://awesome-dsh-plugin.com/p/CH4ACKO3/bites-the-dsh/", + "category": "session", + "description": { + "en": "Adds read-only, scriptable playback to native DSH conversations, with simulated user typing, forward and reverse playback, event stepping, speed control, idle-gap compression, and direct seeking by event, turn, or recorded time.", + "zh": "为 DSH 原生会话加入完全只读、可脚本控制的历史回放,支持模拟用户输入、正放与倒放、逐事件移动、变速、空闲间隔压缩,以及按事件、轮次或记录时间直接跳转。" + }, + "npm": "@ch4acko3/bites-the-dsh", + "stars": 1, + "downloads": 670, + "install": "dsh plugin --profile web add @ch4acko3/bites-the-dsh", + "added": "2026-08-21" + }, + { + "name": "dsh-reference-anything", + "owner": "Chael-Chael", + "url": "https://github.com/Chael-Chael/dsh-reference-anything", + "page": "https://awesome-dsh-plugin.com/p/Chael-Chael/dsh-reference-anything/", + "category": "session", + "description": { + "en": "Reference workspace files, DSH sessions, and historical ChatGPT, Claude, Gemini, DeepSeek, Grok, and Kimi conversations from the unified DSH @ menu.", + "zh": "从 DSH 统一的 @ 菜单中引用工作区文件、DSH 会话,以及 ChatGPT、Claude、Gemini、DeepSeek、Grok 和 Kimi 的历史对话。" + }, + "npm": "dsh-reference-anything", + "stars": 10, + "downloads": 1467, + "install": "dsh plugin --profile web add dsh-reference-anything", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/Chael-Chael/dsh-reference-anything/HEAD/images/banner.png", + "https://raw.githubusercontent.com/Chael-Chael/dsh-reference-anything/HEAD/images/at-external-conversations.png", + "https://raw.githubusercontent.com/Chael-Chael/dsh-reference-anything/HEAD/images/at-files.png", + "https://raw.githubusercontent.com/Chael-Chael/dsh-reference-anything/HEAD/images/at-sessions.png", + "https://raw.githubusercontent.com/Chael-Chael/dsh-reference-anything/HEAD/images/at-commands.png", + "https://raw.githubusercontent.com/Chael-Chael/dsh-reference-anything/HEAD/images/at-skills.png" + ] + }, + { + "name": "dsh-history", + "owner": "chenproton", + "url": "https://github.com/chenproton/dsh-history", + "page": "https://awesome-dsh-plugin.com/p/chenproton/dsh-history/", + "category": "session", + "description": { + "en": "Browse every message you sent in the current session: full-history listing with newest-first sort, text filter, one-click copy, and jump to a message (auto-loads earlier history when the target is not yet loaded).", + "zh": "会话历史消息查看:列出当前会话全部你发送的消息,支持排序、搜索、一键复制与点击跳转定位(目标未加载时自动加载更早历史)。" + }, + "npm": "dsh-history", + "stars": 10, + "downloads": 4258, + "install": "dsh plugin --profile web add dsh-history", + "added": "2026-08-18" + }, + { + "name": "dsh-interconnect", + "owner": "Chinesezjc", + "url": "https://github.com/Chinesezjc/dsh-interconnect", + "page": "https://awesome-dsh-plugin.com/p/Chinesezjc/dsh-interconnect/", + "category": "session", + "description": { + "en": "Cross-instance message and event handoff between DSH instances via an interconnect server.", + "zh": "跨实例互联:经 interconnect 服务在多个 DSH 实例间转发消息与事件。" + }, + "npm": null, + "stars": 34, + "downloads": null, + "install": "dsh plugin --profile web add github:Chinesezjc/dsh-interconnect", + "added": "2026-08-14" + }, + { + "name": "dsh-branch-review", + "owner": "chouyong", + "url": "https://github.com/chouyong/dsh-branch-review", + "page": "https://awesome-dsh-plugin.com/p/chouyong/dsh-branch-review/", + "category": "session", + "description": { + "en": "Track human decisions for related DSH session branches: keep, discard, or follow up with reasons, labels, and external links.", + "zh": "为相关 DSH 会话分支记录人工决策:保留、淘汰或待跟进,并保存理由、标签与外部链接。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:chouyong/dsh-branch-review", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/chouyong/dsh-branch-review/main/assets/branch-review-desktop.png", + "https://raw.githubusercontent.com/chouyong/dsh-branch-review/main/assets/branch-review-decisions.png" + ] + }, + { + "name": "dsh-fork-diff", + "owner": "chouyong", + "url": "https://github.com/chouyong/dsh-fork-diff", + "page": "https://awesome-dsh-plugin.com/p/chouyong/dsh-fork-diff/", + "category": "session", + "description": { + "en": "Read-only side-by-side comparison of related DeepSeek Harness conversation histories, including messages, tool calls, responses, timing, and usage.", + "zh": "只读并排比较相关 DeepSeek Harness 会话历史,展示消息、工具调用、回答、耗时和 usage 差异。" + }, + "npm": null, + "tarball": "https://github.com/chouyong/dsh-fork-diff/releases/download/v0.1.0/dsh-fork-diff-0.1.0.tgz", + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/chouyong/dsh-fork-diff/releases/download/v0.1.0/dsh-fork-diff-0.1.0.tgz\"", + "added": "2026-08-17", + "screenshots": [ + "https://raw.githubusercontent.com/chouyong/dsh-fork-diff/main/assets/fork-diff-desktop.png", + "https://raw.githubusercontent.com/chouyong/dsh-fork-diff/main/assets/fork-diff-selector.png", + "https://raw.githubusercontent.com/chouyong/dsh-fork-diff/main/assets/fork-diff-mobile.png" + ] + }, + { + "name": "dsh-fork-graph", + "owner": "chouyong", + "url": "https://github.com/chouyong/dsh-fork-graph", + "page": "https://awesome-dsh-plugin.com/p/chouyong/dsh-fork-graph/", + "category": "session", + "description": { + "en": "Git-style conversation fork graph in the session header: colored lanes and fork curves show which session branched from which, with click-to-jump navigation.", + "zh": "会话标题栏内联的 Git 风格 fork 血缘图:用彩色轨道与分叉曲线显示会话从何处分支,并可点击跳转。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:chouyong/dsh-fork-graph", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/chouyong/dsh-fork-graph/main/assets/screenshot-trigger.png", + "https://raw.githubusercontent.com/chouyong/dsh-fork-graph/main/assets/screenshot-panel.png" + ] + }, + { + "name": "dsh-local-share", + "owner": "ChuanTianML", + "url": "https://github.com/ChuanTianML/dsh-local-share", + "page": "https://awesome-dsh-plugin.com/p/ChuanTianML/dsh-local-share/", + "category": "session", + "description": { + "en": "Export complete Web Sessions to local Markdown or self-contained HTML, with stable rendered previews, default redaction, and opt-in bounded tool-call details.", + "zh": "把完整 Web Session 导出为本地 Markdown 或自包含 HTML,提供稳定的 Markdown 渲染预览、默认脱敏和可选的有界工具调用详情。" + }, + "npm": "dsh-local-share", + "stars": 1, + "downloads": 295, + "install": "dsh plugin --profile web add dsh-local-share", + "added": "2026-08-15" + }, + { + "name": "dsh-archive-manager", + "owner": "ChuShiZ", + "url": "https://github.com/ChuShiZ/dsh-archive-manager", + "page": "https://awesome-dsh-plugin.com/p/ChuShiZ/dsh-archive-manager/", + "category": "session", + "description": { + "en": "Archived-session manager for DSH Web: workspace-grouped list, FTS5 full-text search over session content with per-session snippets, restore-and-open, and hard delete.", + "zh": "归档会话管理插件:按工作区分组浏览,FTS5 全文检索会话内容并展示多条命中摘要,支持恢复并打开与彻底删除。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:ChuShiZ/dsh-archive-manager", + "added": "2026-08-26" + }, + { + "name": "dsh-webchatlike", + "owner": "cindyguyuehu123", + "url": "https://github.com/cindyguyuehu123/dsh-webchatlike", + "page": "https://awesome-dsh-plugin.com/p/cindyguyuehu123/dsh-webchatlike/", + "category": "session", + "description": { + "en": "Bring the deepseek.com web/app chat experience to DSH: edit your prompt and regenerate answers in place, with a per-message version pager (tree model, stable across conversations).", + "zh": "更贴近 deepseek 网页版/App 的聊天体验:原位编辑提问、重新生成回复、每条消息带 版本翻页器(树状版本模型,跨对话保持稳定)。" + }, + "npm": null, + "stars": 6, + "downloads": null, + "install": "dsh plugin --profile web add github:cindyguyuehu123/dsh-webchatlike", + "added": "2026-08-14" + }, + { + "name": "dsh-session-recovery", + "owner": "Coprexist", + "url": "https://github.com/Coprexist/dsh-session-recovery", + "page": "https://awesome-dsh-plugin.com/p/Coprexist/dsh-session-recovery/", + "category": "session", + "description": { + "en": "Recover deleted or corrupted dsh sessions (session.jsonl.zstd) and memory (memory.db) from the raw disk, and repair rebuilt sessions that fail to resume via the /session-repair command in the web UI.", + "zh": "从原始磁盘恢复被删除或损坏的 dsh 会话(session.jsonl.zstd)与记忆库(memory.db),并通过 web 界面中的 /session-repair 命令修复重建后无法续接的会话。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Coprexist/dsh-session-recovery", + "added": "2026-08-18" + }, + { + "name": "dsh-peer-link", + "owner": "czm15053", + "url": "https://github.com/czm15053/dsh-peer-link", + "page": "https://awesome-dsh-plugin.com/p/czm15053/dsh-peer-link/", + "category": "session", + "description": { + "en": "Let dsh and Claude Code sessions message each other directly; comes with a clickable peer list card (sort/search/send/refresh).", + "zh": "让 dsh 和 Claude Code 会话直接互发消息,附带可点击的会话列表卡片(搜索/刷新/弹窗发送)。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:czm15053/dsh-peer-link", + "added": "2026-08-14" + }, + { + "name": "dsh-projection-guard", + "owner": "DamonKoy", + "url": "https://github.com/DamonKoy/dsh-projection-guard", + "page": "https://awesome-dsh-plugin.com/p/DamonKoy/dsh-projection-guard/", + "category": "session", + "description": { + "en": "Guards the persisted session-projection cache: per-row JSON degradation so one misbehaving projection unit (e.g. a third-party plugin storing a Map) can never stall session titles or the whole cache again, plus a startup self-heal that backfills missing titles.", + "zh": "会话投影缓存守卫:逐行 JSON 降级,单个违规投影单元(如第三方插件存储 Map)不再拖垮会话标题与整个缓存;启动自愈自动补全缺失标题。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:DamonKoy/dsh-projection-guard", + "added": "2026-08-17" + }, + { + "name": "task-passport", + "owner": "dongsheng123132", + "url": "https://github.com/dongsheng123132/task-passport", + "page": "https://awesome-dsh-plugin.com/p/dongsheng123132/task-passport/", + "category": "session", + "description": { + "en": "Carry durable task state across DeepSeek Harness, WorkBuddy, Claude Code and Codex with machine-readable checkpoints and optimistic locking.", + "zh": "通过机器可读检查点与乐观锁,在 DeepSeek Harness、WorkBuddy、Claude Code 和 Codex 之间交接持久任务状态。" + }, + "npm": "task-passport", + "stars": 10, + "downloads": 899, + "install": "dsh plugin --profile web add task-passport", + "added": "2026-08-14" + }, + { + "name": "dsh-session-manager", + "owner": "dream12347", + "url": "https://github.com/dream12347/dsh-session-manager", + "page": "https://awesome-dsh-plugin.com/p/dream12347/dsh-session-manager/", + "category": "session", + "description": { + "en": "DSH session manager: delete with trash/restore/purge, restore archived sessions, recent-activity stats, continue/pause, open log folder, unread markers, fork into a new chat, workspace grouping and sorting, context compaction threshold.", + "zh": "DSH 会话管理插件:删除(回收站可恢复或彻底清除)、恢复归档会话、活动统计、继续/暂停、打开日志目录、未读标记、新聊天中继续、工作区分组与排序、上下文压缩阈值设置。" + }, + "npm": null, + "stars": 50, + "downloads": null, + "install": "dsh plugin --profile web add github:dream12347/dsh-session-manager", + "added": "2026-08-16" + }, + { + "name": "dsh-session-guard", + "owner": "drscrewdriver", + "url": "https://github.com/drscrewdriver/dsh-session-guard", + "page": "https://awesome-dsh-plugin.com/p/drscrewdriver/dsh-session-guard/", + "category": "session", + "description": { + "en": "Peak-hour auto session gate: weekend mode + global peak auto-pause + session-level freeze/resume + backend auto-retry, auto-resume on off-peak.", + "zh": "高峰自动会话门:周末模式 + 高峰自动暂停(全局)+ 会话级冻结/恢复 + 后端自动重试;错峰自动续跑。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:drscrewdriver/dsh-session-guard", + "added": "2026-08-27" + }, + { + "name": "dsh-session-search-toggle", + "owner": "drscrewdriver", + "url": "https://github.com/drscrewdriver/dsh-session-search-toggle", + "page": "https://awesome-dsh-plugin.com/p/drscrewdriver/dsh-session-search-toggle/", + "category": "session", + "description": { + "en": "Sidebar session-search toggle between title and content: title filters by session title, content searches historical messages through DSH's built-in full-text index, grouped per session with the strongest snippet and message-type tags.", + "zh": "侧边栏会话搜索开关,标题 / 内容一键切换:标题按会话标题过滤,内容经 DSH 自带全文索引搜索历史消息关键词,结果按会话聚合,附最强命中片段与消息类型标签。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:drscrewdriver/dsh-session-search-toggle", + "added": "2026-08-23" + }, + { + "name": "dsh-session-unarchive", + "owner": "dylan121322", + "url": "https://github.com/dylan121322/dsh-session-unarchive", + "page": "https://awesome-dsh-plugin.com/p/dylan121322/dsh-session-unarchive/", + "category": "session", + "description": { + "en": "Restore archived sessions to their original workspace from the Web GUI sidebar.", + "zh": "从 Web GUI 侧栏查看已归档会话,并一键恢复到原工作区。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:dylan121322/dsh-session-unarchive", + "added": "2026-08-15" + }, + { + "name": "dsh-archive-manager", + "owner": "Earnest02522", + "url": "https://github.com/Earnest02522/dsh-archive-manager", + "page": "https://awesome-dsh-plugin.com/p/Earnest02522/dsh-archive-manager/", + "category": "session", + "description": { + "en": "Archived-conversation manager for DeepSeek Harness: view archived conversations grouped by workspace, restore them, and reveal their transcript folders.", + "zh": "归档会话管理器:按工作区分组查看已归档的会话,支持恢复(取消归档)与打开会话转录目录。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Earnest02522/dsh-archive-manager", + "added": "2026-08-26" + }, + { + "name": "dsh-context-proxy", + "owner": "EvilIrving", + "url": "https://github.com/EvilIrving/dsh-context-proxy", + "page": "https://awesome-dsh-plugin.com/p/EvilIrving/dsh-context-proxy/", + "category": "session", + "description": { + "en": "Thin on-demand context retrieval: context_query / context_slice / context_grep tools that read already-persisted history back with replay-safe citations.", + "zh": "按需取回薄层:context_query / context_slice / context_grep 三个工具读取已持久化的历史,引用可回放。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:EvilIrving/dsh-context-proxy", + "added": "2026-08-14" + }, + { + "name": "dsh-force-compact", + "owner": "falling-ts", + "url": "https://github.com/falling-ts/dsh-force-compact", + "page": "https://awesome-dsh-plugin.com/p/falling-ts/dsh-force-compact/", + "category": "session", + "description": { + "en": "Forces context compression when running low-context llama.cpp models such as Qwen3.8-27B, improving model efficiency and usage smoothness, together with well-refined compaction prompts.", + "zh": "llama-cpp qwen3.8-27b 低上下文时,强制上下文压缩插件,提示模型使用效率和使用流畅度,外加完善的压缩提示。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:falling-ts/dsh-force-compact", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/falling-ts/dsh-force-compact/HEAD/assets/settings-panel.png", + "https://raw.githubusercontent.com/falling-ts/dsh-force-compact/HEAD/assets/live-conversation.png" + ] + }, + { + "name": "dsh-input-rewriter", + "owner": "feiertu", + "url": "https://github.com/feiertu/dsh-input-rewriter", + "page": "https://awesome-dsh-plugin.com/p/feiertu/dsh-input-rewriter/", + "category": "session", + "description": { + "en": "Type normally; about two seconds after you pause, it rewrites your draft into a version more likely to get a good answer, leaving pasted code, links and long text untouched and keeping multi-part questions numbered.", + "zh": "你只管正常打字,停下约两秒,它就把你刚写的话自动改写成更容易得到好答案的样子;贴进来的代码、网址、长文原样不动,一次问几件事也一件不漏、排好顺序。" + }, + "npm": "dsh-input-rewriter", + "stars": 1, + "downloads": 457, + "install": "dsh plugin --profile web add dsh-input-rewriter", + "added": "2026-08-19" + }, + { + "name": "dsh-msg-rewind", + "owner": "flow2dream", + "url": "https://github.com/flow2dream/dsh-msg-rewind", + "page": "https://awesome-dsh-plugin.com/p/flow2dream/dsh-msg-rewind/", + "category": "session", + "description": { + "en": "Conversation rewind for dsh web: /undo rolls back one turn, /rewind cuts to a past message, and hovering a user bubble offers a visual undo.", + "zh": "对话回退:/undo 回退一轮,/rewind 回退到历史消息,悬浮用户气泡可可视化撤回。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:flow2dream/dsh-msg-rewind", + "added": "2026-08-19" + }, + { + "name": "dsh-solo-thinking", + "owner": "fredalxin", + "url": "https://github.com/fredalxin/dsh-solo-thinking", + "page": "https://awesome-dsh-plugin.com/p/fredalxin/dsh-solo-thinking/", + "category": "session", + "description": { + "en": "Visual branch brainstorming that creates an isolated Session for each direction, automates parent, sibling, checkpoint, and return Handoffs, and provides a full tree tab with an optional Better Sidebar view.", + "zh": "可视化分支头脑风暴:为每个方向创建独立 Session,自动处理父子继承、兄弟感知、进展与回传 Handoff,并提供完整树形 Tab 与可选 Better Sidebar 右栏。" + }, + "npm": "dsh-plugin-solo-thinking", + "stars": 21, + "downloads": 443, + "install": "dsh plugin --profile web add dsh-plugin-solo-thinking", + "added": "2026-08-15" + }, + { + "name": "dsh-client-auto-retry", + "owner": "Frog755", + "url": "https://github.com/Frog755/dsh-client-auto-retry", + "page": "https://awesome-dsh-plugin.com/p/Frog755/dsh-client-auto-retry/", + "category": "session", + "description": { + "en": "Auto-resumes interrupted DSH turns: sends a queued 「继续」 after turn/end with error, interrupted or max-tokens reasons, with a grace period, cooldown, a consecutive-attempts cap, boot-time scanning and a settings card; it never switches models or providers.", + "zh": "回合中断自动续跑:turn/end 因 error / interrupted / max-tokens 结束时自动发送「继续」,支持宽限期、冷却、连续次数上限、启动扫描与设置卡片;不切换模型/provider。" + }, + "npm": "@frog755/dsh-client-auto-retry", + "stars": 2, + "downloads": 338, + "install": "dsh plugin --profile web add @frog755/dsh-client-auto-retry", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/Frog755/dsh-client-auto-retry/HEAD/assets/demo.svg", + "https://raw.githubusercontent.com/Frog755/dsh-client-auto-retry/HEAD/assets/screenshot-settings-card.png" + ] + }, + { + "name": "dsh-agent-message", + "owner": "GengDaPeng", + "url": "https://github.com/GengDaPeng/dsh-agent-message", + "page": "https://awesome-dsh-plugin.com/p/GengDaPeng/dsh-agent-message/", + "category": "session", + "description": { + "en": "Cross-session agent messaging for DeepSeek Harness with session discovery, offline delivery, delivery receipts, and sender-session navigation.", + "zh": "DeepSeek Harness 跨会话 Agent 通信:支持会话发现、离线投递、投递回执与发送方会话导航。" + }, + "npm": "dsh-agent-message", + "stars": 5, + "downloads": 477, + "install": "dsh plugin --profile web add dsh-agent-message", + "added": "2026-08-15" + }, + { + "name": "dsh-session-insights", + "owner": "GreenLv", + "url": "https://github.com/GreenLv/dsh-session-insights", + "page": "https://awesome-dsh-plugin.com/p/GreenLv/dsh-session-insights/", + "category": "session", + "description": { + "en": "Generate local, evidence-backed workflow retrospectives from DSH session history through `/session-insights`, with deterministic and optional model-assisted analysis.", + "zh": "通过 `/session-insights` 将 DSH 会话历史生成为本地、基于证据的工作流复盘,支持确定性分析和可选的模型辅助分析。" + }, + "npm": "dsh-session-insights", + "stars": 1, + "downloads": 157, + "install": "dsh plugin --profile web add dsh-session-insights", + "added": "2026-08-23", + "screenshots": [ + "https://raw.githubusercontent.com/GreenLv/dsh-session-insights/HEAD/assets/screenshots/dashboard-overview-en.png", + "https://raw.githubusercontent.com/GreenLv/dsh-session-insights/HEAD/assets/screenshots/dashboard-overview-zh.png" + ] + }, + { + "name": "DSH-Session-Sync", + "owner": "hajimimaodie8", + "url": "https://github.com/hajimimaodie8/DSH-Session-Sync", + "page": "https://awesome-dsh-plugin.com/p/hajimimaodie8/DSH-Session-Sync/", + "category": "session", + "description": { + "en": "Bidirectionally synchronize sessions, workspaces, settings and plugins between the DSH web instance and the DSH Desktop instance.", + "zh": "双向同步网页版与 DSH Desktop 桌面版之间的会话、工作区、设置与插件。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:hajimimaodie8/DSH-Session-Sync", + "added": "2026-08-16" + }, + { + "name": "dsh-session-toolkit", + "owner": "Han-Yao94", + "url": "https://github.com/Han-Yao94/dsh-session-toolkit", + "page": "https://awesome-dsh-plugin.com/p/Han-Yao94/dsh-session-toolkit/", + "category": "session", + "description": { + "en": "Consolidated toolkit for session identity, global & workspace prompts with referenced files, session auto-resume, UAC-free web restart, and peer-session messaging.", + "zh": "整合工具包:会话身份、全局/工作区提示词(含引用文件)、会话自动上线、免 UAC 网页重启、会话间消息。" + }, + "npm": "dsh-session-toolkit", + "stars": 2, + "downloads": 586, + "install": "dsh plugin --profile web add dsh-session-toolkit", + "added": "2026-08-24" + }, + { + "name": "dsh-session-cleaner", + "owner": "haoranwang0921", + "url": "https://github.com/haoranwang0921/dsh-session-cleaner", + "page": "https://awesome-dsh-plugin.com/p/haoranwang0921/dsh-session-cleaner/", + "category": "session", + "description": { + "en": "Manage conversation records from the DSH web GUI settings page: delete a whole session (archive plus physical log removal) or a single message (deleting a user message cascades to its assistant replies and tool results). Running and current sessions are protected.", + "zh": "在 DSH Web GUI 设置页管理对话记录:可删除整个会话(归档并物理删除日志),或按用户消息分组浏览并删除单条消息(删除用户消息会级联删除其引发的助手与工具消息)。运行中与当前会话受保护。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:haoranwang0921/dsh-session-cleaner", + "added": "2026-08-23" + }, + { + "name": "dsh-side-chat", + "owner": "heartmove", + "url": "https://github.com/heartmove/dsh-side-chat", + "page": "https://awesome-dsh-plugin.com/p/heartmove/dsh-side-chat/", + "category": "session", + "description": { + "en": "Select part of a conversation and ask about it in a right-side side chat; bring AI replies back to the main chat directly or as a summary.", + "zh": "选中对话片段,在右侧面板的侧边聊天中提问(按会话隔离);AI 回复可原文或摘要后带回主会话。" + }, + "npm": null, + "stars": 12, + "downloads": null, + "install": "dsh plugin --profile web add github:heartmove/dsh-side-chat", + "added": "2026-08-14" + }, + { + "name": "dsh-plugin-subagent-delete", + "owner": "heiheiha798", + "url": "https://github.com/heiheiha798/dsh-plugin-subagent-delete", + "page": "https://awesome-dsh-plugin.com/p/heiheiha798/dsh-plugin-subagent-delete/", + "category": "session", + "description": { + "en": "Ownership-checked tools and HTTP routes to list, release and permanently delete subagent sessions, with automatic web UI refresh after deletion.", + "zh": "带归属校验的工具与 HTTP 路由,用于列出、释放和永久删除子代理会话,删除后自动刷新 web UI。" + }, + "npm": null, + "tarball": "https://github.com/heiheiha798/dsh-plugin-subagent-delete/releases/latest/download/dsh-plugin-subagent-delete-0.2.0.tgz", + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/heiheiha798/dsh-plugin-subagent-delete/releases/latest/download/dsh-plugin-subagent-delete-0.2.0.tgz\"", + "added": "2026-08-24" + }, + { + "name": "dsh-share", + "owner": "hellodigua", + "url": "https://github.com/hellodigua/dsh-share", + "page": "https://awesome-dsh-plugin.com/p/hellodigua/dsh-share/", + "category": "session", + "description": { + "en": "Share your conversations with one click.", + "zh": "一键分享你的对话。" + }, + "npm": "dsh-share", + "stars": 35, + "downloads": 1323, + "install": "dsh plugin --profile web add dsh-share", + "added": "2026-08-13" + }, + { + "name": "dsh-session-manager", + "owner": "hkkz9522", + "url": "https://github.com/hkkz9522/dsh-session-manager", + "page": "https://awesome-dsh-plugin.com/p/hkkz9522/dsh-session-manager/", + "category": "session", + "description": { + "en": "Delete conversations with a confirmation dialog and manage archived sessions (archive/unarchive) in the DeepSeek Harness web UI.", + "zh": "在 DeepSeek Harness Web 中删除对话(二次确认),并管理归档会话(移入/移出归档)。" + }, + "npm": "dsh-session-manager", + "stars": 3, + "downloads": 1980, + "install": "dsh plugin --profile web add dsh-session-manager", + "added": "2026-08-17" + }, + { + "name": "dsh-session-categories", + "owner": "ht719", + "url": "https://github.com/ht719/dsh-session-categories", + "page": "https://awesome-dsh-plugin.com/p/ht719/dsh-session-categories/", + "category": "session", + "description": { + "en": "Adds per-Workspace category trees to the Web sidebar, with nested categories, drag-and-drop session assignment, category-scoped session creation, and category deletion that archives contained sessions.", + "zh": "为 Web 侧边栏添加按 Workspace 隔离的分类树,支持嵌套分类、拖拽归类会话、在分类中新建会话,以及删除分类时归档其中的会话。" + }, + "npm": "dsh-session-categories", + "stars": 5, + "downloads": 183, + "install": "dsh plugin --profile web add dsh-session-categories", + "added": "2026-08-22" + }, + { + "name": "dsh-session-manager", + "owner": "huajuan2024", + "url": "https://github.com/huajuan2024/dsh-session-manager", + "page": "https://awesome-dsh-plugin.com/p/huajuan2024/dsh-session-manager/", + "category": "session", + "description": { + "en": "Session manager panel under Settings → Plugins: list all sessions with title, cwd, agent preset, updated time, turns and steps, expand a row to view message history, delete non-running sessions, and export a session as JSON or Markdown via a browser Blob URL.", + "zh": "Session manager panel under Settings → Plugins: list all sessions with title, cwd, agent preset, updated time, turns and steps, expand a row to view message history, delete non-running sessions, and export a session as JSON or Markdown via a browser Blob URL." + }, + "npm": null, + "stars": 0, + "downloads": null, + "install": "dsh plugin --profile web add github:huajuan2024/dsh-session-manager", + "added": "2026-08-25" + }, + { + "name": "dsh-plugin-session-import", + "owner": "huguangyu666", + "url": "https://github.com/huguangyu666/dsh-plugin-session-import", + "page": "https://awesome-dsh-plugin.com/p/huguangyu666/dsh-plugin-session-import/", + "category": "session", + "description": { + "en": "Import claude-code / codex / reasonix / zcode chat history into dsh sessions: workspace binding, tool calls preserved, oversized-session protection, zcode compaction restore.", + "zh": "把 claude-code / codex / reasonix / zcode 的聊天历史导入为 dsh 会话:工作区绑定、工具调用保留、超长会话保护、zcode 压缩还原。" + }, + "npm": "dsh-plugin-session-import", + "stars": 6, + "downloads": 560, + "install": "dsh plugin --profile web add dsh-plugin-session-import", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/huguangyu666/dsh-plugin-session-import/main/assets/screenshot.png" + ] + }, + { + "name": "dsh-incognito", + "owner": "HuiHuitie-zhu", + "url": "https://github.com/HuiHuitie-zhu/dsh-incognito", + "page": "https://awesome-dsh-plugin.com/p/HuiHuitie-zhu/dsh-incognito/", + "category": "session", + "description": { + "en": "Incognito sessions: a standalone ephemeral sub-agent with its own preset (no parent context) and full toolset, draggable floating window, burned to zero traces on close.", + "zh": "无痕会话:独立临时子 Agent(独立 preset 不继承父会话、全量工具集),浮窗可拖动,关闭即焚毁、磁盘零痕迹。" + }, + "npm": "dsh-incognito", + "stars": 4, + "downloads": 187, + "install": "dsh plugin --profile web add dsh-incognito", + "added": "2026-08-15" + }, + { + "name": "dsh-plugin-kit#search", + "owner": "hyzyn", + "url": "https://github.com/hyzyn/dsh-plugin-kit/tree/main/packages/search", + "page": "https://awesome-dsh-plugin.com/p/hyzyn/dsh-plugin-kit--packages-search/", + "category": "session", + "description": { + "en": "Full-text global search for the DSH Web GUI: a sidebar search box that searches history sessions (via the sessionQuery index, with a per-session scan fallback) and settings panels, highlights matches, and jumps to the matching session or settings card.", + "zh": "DSH Web GUI 的全局搜索插件:侧边栏全局搜索框全文搜索历史会话(走 sessionQuery 全文索引,未启用时降级为逐会话扫描)与设置面板,关键词高亮,点击直达会话或对应设置卡片。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:hyzyn/dsh-plugin-kit#path:/packages/search", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/hyzyn/dsh-plugin-kit/main/docs/dsh-plugin-kit-search.png" + ] + }, + { + "name": "context-assembler-DSH", + "owner": "i1j", + "url": "https://github.com/i1j/context-assembler-DSH", + "page": "https://awesome-dsh-plugin.com/p/i1j/context-assembler-DSH/", + "category": "session", + "description": { + "en": "Context Assembler plugin for DeepSeek Harness: topic-block context assembly, water-pressure topic splitting, tool trace/rewrite, reality recall injection and handoff planning.", + "zh": "DeepSeek Harness 上下文汇编插件:话题块上下文汇编、水位压力话题切割、工具轮压缩/改写、reality 召回注入与 handoff 规划。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:i1j/context-assembler-DSH", + "added": "2026-08-21" + }, + { + "name": "dsh-archive", + "owner": "imrascal", + "url": "https://github.com/imrascal/dsh-archive", + "page": "https://awesome-dsh-plugin.com/p/imrascal/dsh-archive/", + "category": "session", + "description": { + "en": "DeepSeek Harness plugin that manages archived sessions and the trash from the settings panel.", + "zh": "DeepSeek Harness 存档会话管理插件:在设置面板管理归档会话(恢复/删除)与回收站。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:imrascal/dsh-archive", + "added": "2026-08-27" + }, + { + "name": "dsh-rolehub-bridge", + "owner": "ishuowang", + "url": "https://github.com/ishuowang/dsh-rolehub-bridge", + "page": "https://awesome-dsh-plugin.com/p/ishuowang/dsh-rolehub-bridge/", + "category": "session", + "description": { + "en": "Discover and verify portable RoleHub roles, pin each exact bundle, then launch its prompt and bundled skills in an isolated, continuable DSH Session with Host-approved tool bindings and optional Agent Team Room attachment.", + "zh": "发现并校验可移植的 RoleHub 角色,固定其精确 bundle,再将提示词和随角色打包的 Skills 加载到独立、可续聊的 DSH Session,以 Host 批准的工具绑定限制能力,并可选接入 Agent Team Room。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:ishuowang/dsh-rolehub-bridge", + "added": "2026-08-15" + }, + { + "name": "dsh-sideband", + "owner": "ishuowang", + "url": "https://github.com/ishuowang/dsh-sideband", + "page": "https://awesome-dsh-plugin.com/p/ishuowang/dsh-sideband/", + "category": "session", + "description": { + "en": "Hand off focused context without waiting for or interrupting the active Agent: freeze a bounded Session snapshot, summarize it asynchronously with an isolated no-tool LLM, and deliver a provenance-bearing capsule to another Session or an authorized Room—quiet by default for Sessions, with explicit wakeup available.", + "zh": "无需等待或打断正在工作的 Agent:即时冻结有界 Session 快照,由隔离且无工具的 LLM 异步总结,再将带来源信息的上下文胶囊投递到另一 Session 或已授权 Room;对 Session 默认 quiet,也可显式 wakeup。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:ishuowang/dsh-sideband", + "added": "2026-08-15" + }, + { + "name": "dsh-session-fork", + "owner": "Jason-skd", + "url": "https://github.com/Jason-skd/dsh-session-fork", + "page": "https://awesome-dsh-plugin.com/p/Jason-skd/dsh-session-fork/", + "category": "session", + "description": { + "en": "Git-style conversation branching: adopt the current session as a branch, fork branches at any turn via /branch commands or the branch tab, and squash a branch back into another branch.", + "zh": "Git 式会话分支:把当前会话收编为分支,通过 /branch 命令或分支页签在任意轮次分叉,并把分支 squash 回另一条分支。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:Jason-skd/dsh-session-fork", + "added": "2026-08-23" + }, + { + "name": "dsh-btw-plugin", + "owner": "JasonQQ", + "url": "https://github.com/JasonQQ/dsh-btw-plugin", + "page": "https://awesome-dsh-plugin.com/p/JasonQQ/dsh-btw-plugin/", + "category": "session", + "description": { + "en": "Codex-style /btw command: answer side questions in a conversation-seeded subagent (fork provider), rendered in the UI without entering the main session history.", + "zh": "Codex 风格 /btw 指令:在继承主会话上下文的子代理中回答附带问题,答案展示在界面里、不写入主会话历史。" + }, + "npm": null, + "tarball": "https://github.com/JasonQQ/dsh-btw-plugin/releases/download/v0.1.0/jasonqq-dsh-btw-plugin-0.1.0.tgz", + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/JasonQQ/dsh-btw-plugin/releases/download/v0.1.0/jasonqq-dsh-btw-plugin-0.1.0.tgz\"", + "added": "2026-08-27" + }, + { + "name": "dsh-archive-manager", + "owner": "jasonrale", + "url": "https://github.com/jasonrale/dsh-archive-manager", + "page": "https://awesome-dsh-plugin.com/p/jasonrale/dsh-archive-manager/", + "category": "session", + "description": { + "en": "Archived-session manager for DSH Web UI: reopen an archived session and keep chatting, unarchive it back in place, or hard-delete a session — grouped panel with message search and native view sync.", + "zh": "DSH Web UI 的归档会话管理器:重新打开已归档会话并继续对话、取消归档恢复原位、彻底删除会话,支持分组浏览、消息搜索与原生视图同步。" + }, + "npm": "dsh-archive-manager", + "stars": 6, + "downloads": 757, + "install": "dsh plugin --profile web add dsh-archive-manager", + "added": "2026-08-16" + }, + { + "name": "agent-context-editor#deepseek-harness", + "owner": "jermaine123123", + "url": "https://github.com/jermaine123123/agent-context-editor/tree/main/adapters/deepseek-harness", + "page": "https://awesome-dsh-plugin.com/p/jermaine123123/agent-context-editor--adapters-deepseek-harness/", + "category": "session", + "description": { + "en": "Agent Context Editor is a cross-agent plugin for manually excluding and editing AI conversation context. It also supports searching, filtering, selecting, hiding, restoring, and undoing conversation changes while preserving the original Session.", + "zh": "Agent Context Editor 是一个可以手动排除和编辑 AI 对话上下文的跨 Agent 插件,同时支持搜索、筛选、选择、隐藏、恢复和撤销对话内容,并保留原始 Session。" + }, + "npm": null, + "tarball": "https://github.com/jermaine123123/agent-context-editor/releases/download/v0.3.0/context-editor-deepseek-harness-0.3.0.tgz", + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/jermaine123123/agent-context-editor/releases/download/v0.3.0/context-editor-deepseek-harness-0.3.0.tgz\"", + "added": "2026-08-19" + }, + { + "name": "dsh-crosstalk", + "owner": "Jesse-njx", + "url": "https://github.com/Jesse-njx/dsh-crosstalk", + "page": "https://awesome-dsh-plugin.com/p/Jesse-njx/dsh-crosstalk/", + "category": "session", + "description": { + "en": "Cross-session messaging for DSH: any session on the machine can list and message any other, Claude Code-style, via a local heartbeat registry and inbox.", + "zh": "跨会话消息:本机任意会话都可像 Claude Code 一样列出并互发消息,基于本地心跳注册表与收件箱。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Jesse-njx/dsh-crosstalk", + "added": "2026-08-14" + }, + { + "name": "session-titler", + "owner": "JohnXu22786", + "url": "https://github.com/JohnXu22786/session-titler", + "page": "https://awesome-dsh-plugin.com/p/JohnXu22786/session-titler/", + "category": "session", + "description": { + "en": "Two-phase session captioning: instant keyword titles while the conversation is busy, then cheapest-budget-model refinement plus a one-line summary once it goes idle.", + "zh": "两阶段会话题词:会话进行中即时关键词题名,空闲后再调用最经济的预算模型精修,并附带一句话摘要。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:JohnXu22786/session-titler", + "added": "2026-08-16" + }, + { + "name": "dsh-win-minimal", + "owner": "jueburenshu123", + "url": "https://github.com/jueburenshu123/dsh-win-minimal", + "page": "https://awesome-dsh-plugin.com/p/jueburenshu123/dsh-win-minimal/", + "category": "session", + "description": { + "en": "Windows minimal agent preset for DeepSeek Harness: fixed one-line persona, three tools, no runtime context.", + "zh": "Windows 极简模式 preset:单句 persona、三工具、无运行时上下文。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:jueburenshu123/dsh-win-minimal", + "added": "2026-08-22" + }, + { + "name": "dsh-AIR", + "owner": "kaieye", + "url": "https://github.com/kaieye/dsh-AIR", + "page": "https://awesome-dsh-plugin.com/p/kaieye/dsh-AIR/", + "category": "session", + "description": { + "en": "Web composer enhancement: recalls sent prompts across sessions with ArrowUp/ArrowDown, searches them with Ctrl+R, restores rich drafts, and opens a docked /btw or /side conversation.", + "zh": "DSH Web 输入框增强:跨会话召回已发送提示词、用 Ctrl+R 搜索并还原富草稿,以及提供停靠式 /btw 或 /side 旁路会话。" + }, + "npm": "dsh-air", + "stars": 2, + "downloads": 555, + "install": "dsh plugin --profile web add dsh-air", + "added": "2026-08-19" + }, + { + "name": "dsh-assembly.resume", + "owner": "kanchengw", + "url": "https://github.com/kanchengw/dsh-assembly.resume", + "page": "https://awesome-dsh-plugin.com/p/kanchengw/dsh-assembly.resume/", + "category": "session", + "description": { + "en": "Import local Codex CLI/Desktop, Claude Code CLI, and Claude Desktop conversations into DSH with project grouping, semantic history, and tool activity; bind an existing or replacement workspace and continue with the DSH Agent.", + "zh": "将本地 Codex CLI/Desktop、Claude Code CLI 和 Claude Desktop 会话导入 DSH:按项目分组,保留语义历史和工具活动,可绑定原工作区或新工作区,并由 DSH Agent 继续对话。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:kanchengw/dsh-assembly.resume", + "added": "2026-08-27" + }, + { + "name": "dsh-archive-viewer", + "owner": "keepermttl", + "url": "https://github.com/keepermttl/dsh-archive-viewer", + "page": "https://awesome-dsh-plugin.com/p/keepermttl/dsh-archive-viewer/", + "category": "session", + "description": { + "en": "Archived-session manager for the DSH Web GUI: search titles and message content, tag/filter sessions, read logs, restore sessions, and export ZIP archives.", + "zh": "DSH Web 归档会话管理器:支持标题/内容搜索、标签筛选、阅读日志、恢复会话与 ZIP 导出。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:keepermttl/dsh-archive-viewer", + "added": "2026-08-19" + }, + { + "name": "dsh-session-archive", + "owner": "kiligzzz", + "url": "https://github.com/kiligzzz/dsh-session-archive", + "page": "https://awesome-dsh-plugin.com/p/kiligzzz/dsh-session-archive/", + "category": "session", + "description": { + "en": "Sidebar panel for archived sessions in DeepSeek Harness: group by workspace, search by title, preview user questions, one-click restore, or delete with confirmation.", + "zh": "DeepSeek Harness 已归档会话管理面板:按工作区目录分组、按标题实时搜索、只读预览用户问题、一键恢复,或二次确认后删除。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:kiligzzz/dsh-session-archive", + "added": "2026-08-25" + }, + { + "name": "dsh-session-nav", + "owner": "kiligzzz", + "url": "https://github.com/kiligzzz/dsh-session-nav", + "page": "https://awesome-dsh-plugin.com/p/kiligzzz/dsh-session-nav/", + "category": "session", + "description": { + "en": "Piano-key style in-conversation navigation bar: one key per user message, hover preview tooltip, click-to-jump across full history. Official dual-face dsh plugin, no dsh source changes.", + "zh": "钢琴键风格的会话内导航条:每根键对应一条用户消息,悬停预览、点击跳转到完整历史的任意消息。官方双面 dsh 插件,不改 DSH 源码。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:kiligzzz/dsh-session-nav", + "added": "2026-08-25" + }, + { + "name": "CiteCiter#citeciter", + "owner": "kirkchinese", + "url": "https://github.com/kirkchinese/CiteCiter/tree/main/packages/citeciter", + "page": "https://awesome-dsh-plugin.com/p/kirkchinese/CiteCiter--packages-citeciter/", + "category": "session", + "description": { + "en": "Select text in a committed DSH response to open an independent, persistent, read-only multi-turn Topic bound to source-session evidence and project files while the source Agent keeps running.", + "zh": "选中 DSH 已提交回复中的文字,开启独立、持久、只读的多轮 Topic;Topic 绑定来源会话证据与项目文件,来源 Agent 可继续运行。" + }, + "npm": "@kirkchinese/dsh-citeciter", + "stars": 4, + "downloads": 1278, + "install": "dsh plugin --profile web add @kirkchinese/dsh-citeciter", + "added": "2026-08-22", + "screenshots": [ + "https://raw.githubusercontent.com/kirkchinese/CiteCiter/main/assets/demo/citeciter-0.4.0.gif", + "https://raw.githubusercontent.com/kirkchinese/CiteCiter/main/assets/screenshots/citeciter-settings.png" + ] + }, + { + "name": "claude2dsh", + "owner": "kirkchinese", + "url": "https://github.com/kirkchinese/claude2dsh", + "page": "https://awesome-dsh-plugin.com/p/kirkchinese/claude2dsh/", + "category": "session", + "description": { + "en": "Import Claude Code sessions, skills and plugin assets into DSH as native resumable sessions, and export or sync DSH sessions back to Claude Code JSONL.", + "zh": "将 Claude Code 会话、技能与插件资产导入为 DSH 原生可续聊会话,并支持将 DSH 会话导出或同步回 Claude Code JSONL。" + }, + "npm": "claude2dsh", + "stars": 2, + "downloads": 1045, + "install": "dsh plugin --profile web add claude2dsh", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/kirkchinese/claude2dsh/HEAD/assets/migration-wizard.png", + "https://raw.githubusercontent.com/kirkchinese/claude2dsh/HEAD/assets/migration-wizard-en.png", + "https://raw.githubusercontent.com/kirkchinese/claude2dsh/HEAD/assets/migration-preview.png", + "https://raw.githubusercontent.com/kirkchinese/claude2dsh/HEAD/assets/migration-result.png", + "https://raw.githubusercontent.com/kirkchinese/claude2dsh/HEAD/assets/settings-import-export.png", + "https://raw.githubusercontent.com/kirkchinese/claude2dsh/HEAD/assets/settings-hooks-sources.png" + ] + }, + { + "name": "dsh-loop-engine", + "owner": "kuun993", + "url": "https://github.com/kuun993/dsh-loop-engine", + "page": "https://awesome-dsh-plugin.com/p/kuun993/dsh-loop-engine/", + "category": "session", + "description": { + "en": "Adds a Settings dropdown to switch the agent loop engine between the built-in in-process driver, Claude Code CLI, and Codex CLI.", + "zh": "在设置页增加下拉,切换 agent 循环引擎:内置进程内驱动、Claude Code CLI 与 Codex CLI。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:kuun993/dsh-loop-engine", + "added": "2026-08-27" + }, + { + "name": "dsh-session-ref", + "owner": "lacemou", + "url": "https://github.com/lacemou/dsh-session-ref", + "page": "https://awesome-dsh-plugin.com/p/lacemou/dsh-session-ref/", + "category": "session", + "description": { + "en": "Cross-session reference (mention) plugin — paste @[label](dsh-session:…) into any session, including one in another workspace, and the host injects the referenced session snapshot for the model to read; includes a one-click copy-reference button.", + "zh": "跨会话引用插件:在任意会话(含其他工作区)粘贴 @[label](dsh-session:…) 引用,宿主注入被引用会话的快照供模型阅读,并提供一键复制引用按钮。" + }, + "npm": "dsh-session-ref", + "stars": 4, + "downloads": 634, + "install": "dsh plugin --profile web add dsh-session-ref", + "added": "2026-08-19" + }, + { + "name": "dsh-trace-compare", + "owner": "lamost423", + "url": "https://github.com/lamost423/dsh-trace-compare", + "page": "https://awesome-dsh-plugin.com/p/lamost423/dsh-trace-compare/", + "category": "session", + "description": { + "en": "Trace Compare & Live Maze: draw the agent's real exploration — main path, failed or dead-end detours, backtracks, and subagent branches — on one wall-clock timeline; upload 1–2 session logs for a same-axis compare, or watch it grow live in a session tab.", + "zh": "轨迹对比与实时迷宫:把智能体真实的探索过程——主干推进、失败或扑空的支路、折返点与子代理支路——画在同一根墙钟时间轴上;可上传 1–2 个 session log 做同轴对比,或在会话页签里实时生长。" + }, + "npm": "dsh-trace-compare", + "stars": 47, + "downloads": 947, + "install": "dsh plugin --profile web add dsh-trace-compare", + "added": "2026-08-20" + }, + { + "name": "dsh-workspace-drag", + "owner": "lanscer", + "url": "https://github.com/lanscer/dsh-workspace-drag", + "page": "https://awesome-dsh-plugin.com/p/lanscer/dsh-workspace-drag/", + "category": "session", + "description": { + "en": "Drag a conversation onto any workspace group in the sidebar to relocate it between DSH workspaces, updating its cwd, disk location and registry ownership.", + "zh": "把侧边栏中的对话拖到任意工作区分组,即可在 DSH 工作区之间迁移该对话,同步更新其 cwd、磁盘位置与工作区注册表归属。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:lanscer/dsh-workspace-drag", + "added": "2026-08-28" + }, + { + "name": "dsh-messages-sanitizer", + "owner": "Leeminjing", + "url": "https://github.com/Leeminjing/dsh-messages-sanitizer", + "page": "https://awesome-dsh-plugin.com/p/Leeminjing/dsh-messages-sanitizer/", + "category": "session", + "description": { + "en": "Auto-repairs the messages array after a tool-dispatch crash (orphaned tool_calls / tool messages), preventing 400 INVALID_REQUEST session lock-ups.", + "zh": "工具调度崩溃后自动修复 messages 数组(孤儿 tool_calls / tool 消息),防止 400 INVALID_REQUEST 会话卡死。" + }, + "npm": "dsh-messages-sanitizer", + "stars": 3, + "downloads": 110, + "install": "dsh plugin --profile web add dsh-messages-sanitizer", + "added": "2026-08-15" + }, + { + "name": "dsh-session-search-pro", + "owner": "LeslieWylie", + "url": "https://github.com/LeslieWylie/dsh-session-search-pro", + "page": "https://awesome-dsh-plugin.com/p/LeslieWylie/dsh-session-search-pro/", + "category": "session", + "description": { + "en": "Search, list and read past and current sessions through the harness's own `sessionQuery` service: uses the SQLite FTS5 index where a deployment enables it, and falls back to a bounded newest-first scan where it does not.", + "zh": "通过 harness 自带的 `sessionQuery` 服务搜索、列出、读取历史与当前会话:部署启用了 SQLite FTS5 索引时走索引,未启用时回退到有上限的倒序扫描。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:LeslieWylie/dsh-session-search-pro", + "added": "2026-08-14" + }, + { + "name": "dsh-task-relay", + "owner": "LeslieWylie", + "url": "https://github.com/LeslieWylie/dsh-task-relay", + "page": "https://awesome-dsh-plugin.com/p/LeslieWylie/dsh-task-relay/", + "category": "session", + "description": { + "en": "Cross-session task queue with handoff notes: sessions and subagents push, claim, complete and cancel tasks on a shared file-backed queue, and leave a handoff summary for whoever picks up next.", + "zh": "跨会话任务队列与交接摘要:会话与子 agent 在共享的文件队列上投递、认领、完成、取消任务,并留下交接摘要供后续会话查看。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:LeslieWylie/dsh-task-relay", + "added": "2026-08-14" + }, + { + "name": "dsh-synapse", + "owner": "liangmianya", + "url": "https://github.com/liangmianya/dsh-synapse", + "page": "https://awesome-dsh-plugin.com/p/liangmianya/dsh-synapse/", + "category": "session", + "description": { + "en": "Visual, non-linear conversation workspace for DeepSeek Harness — sessions, follow-ups and branches become a browsable conversation map.", + "zh": "DeepSeek Harness 的可视化非线性对话工作区:把会话、追问与分支变成可浏览、可拖拽的对话地图。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:liangmianya/dsh-synapse", + "added": "2026-08-26" + }, + { + "name": "dsh-recall-plugin", + "owner": "limbo947", + "url": "https://github.com/limbo947/dsh-recall-plugin", + "page": "https://awesome-dsh-plugin.com/p/limbo947/dsh-recall-plugin/", + "category": "session", + "description": { + "en": "Recall any user message: rolls the conversation (official session fork) and workspace files (per-message shadow git snapshots) back to before it, with a diff-preview confirmation panel.", + "zh": "消息撤回:在用户消息旁加「撤回」按钮,把对话历史(官方会话 fork)与项目文件(逐消息影子 git 快照)一并回退到该消息发送之前,带差异预览确认面板。" + }, + "npm": "dsh-recall-plugin", + "stars": 25, + "downloads": 2641, + "install": "dsh plugin --profile web add dsh-recall-plugin", + "added": "2026-08-15" + }, + { + "name": "dsh-conversation-exporter", + "owner": "liuyuelintop", + "url": "https://github.com/liuyuelintop/dsh-conversation-exporter", + "page": "https://awesome-dsh-plugin.com/p/liuyuelintop/dsh-conversation-exporter/", + "category": "session", + "description": { + "en": "Export the current DSH conversation, or selected turns, as clean readable Markdown.", + "zh": "将当前 DSH 对话或选定轮次导出为干净、易读的 Markdown。" + }, + "npm": "dsh-conversation-exporter", + "stars": 1, + "downloads": 619, + "install": "dsh plugin --profile web add dsh-conversation-exporter", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/liuyuelintop/dsh-conversation-exporter/main/assets/select-turns.png", + "https://raw.githubusercontent.com/liuyuelintop/dsh-conversation-exporter/main/assets/selective-export-mermaid.png", + "https://raw.githubusercontent.com/liuyuelintop/dsh-conversation-exporter/main/assets/selective-export-result.png" + ] + }, + { + "name": "dsh-humanized-deepseek-maid", + "owner": "loonai321", + "url": "https://github.com/loonai321/dsh-humanized-deepseek-maid", + "page": "https://awesome-dsh-plugin.com/p/loonai321/dsh-humanized-deepseek-maid/", + "category": "session", + "description": { + "en": "Adds a configurable humanized whale-girl maid persona with immersive roleplay, speaking modes, and a lightweight layered memory with query-triggered recall.", + "zh": "为 DSH 添加可配置的女仆鲸鱼娘人格:沉浸式扮演(称呼/自称/说话方式)、防出戏规则,以及借鉴 ALTM 的轻量分层记忆与按需召回。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:loonai321/dsh-humanized-deepseek-maid", + "added": "2026-08-16" + }, + { + "name": "dsh-plugin-session-delete", + "owner": "lsz-asd", + "url": "https://github.com/lsz-asd/dsh-plugin-session-delete", + "page": "https://awesome-dsh-plugin.com/p/lsz-asd/dsh-plugin-session-delete/", + "category": "session", + "description": { + "en": "Delete DSH sessions from the web UI and desktop client: header danger button and session-row menu item with a risk-consent dialog; host endpoint and agent tool remove the session log, projection cache, and workspace accounting.", + "zh": "在 Web UI 与桌面客户端中删除 DSH 会话:头部危险按钮 + 会话行菜单项,风险确认弹窗,宿主端点与 agent 工具同步清理会话日志、投影缓存与工作区记账。" + }, + "npm": null, + "stars": 25, + "downloads": null, + "install": "dsh plugin --profile web add github:lsz-asd/dsh-plugin-session-delete", + "added": "2026-08-14" + }, + { + "name": "dsh-session-status", + "owner": "LucienLL", + "url": "https://github.com/LucienLL/dsh-session-status", + "page": "https://awesome-dsh-plugin.com/p/LucienLL/dsh-session-status/", + "category": "session", + "description": { + "en": "Per-session status labels (built-in active/done/paused plus custom) shown in the sidebar, conversation header, and hover card, with editable colors.", + "zh": "为每个会话设置项目状态标签(内置进行中/已结项/搁置中+自定义标签,颜色可调),在侧栏、对话头部与悬停卡片中显示。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:LucienLL/dsh-session-status", + "added": "2026-08-27" + }, + { + "name": "dsh-chinese-thinking", + "owner": "Max-Null", + "url": "https://github.com/Max-Null/dsh-chinese-thinking", + "page": "https://awesome-dsh-plugin.com/p/Max-Null/dsh-chinese-thinking/", + "category": "session", + "description": { + "en": "Injects one fixed system-prompt section so the agent thinks and replies in Chinese, regardless of the user's language.", + "zh": "注入一条固定 system-prompt 段落,让 Agent 无论用户使用什么语言都始终用中文思考和回复。" + }, + "npm": "@max-null/dsh-chinese-thinking", + "stars": 4, + "downloads": 1142, + "install": "dsh plugin --profile web add @max-null/dsh-chinese-thinking", + "added": "2026-08-16" + }, + { + "name": "dsh-session-doctor", + "owner": "mayf3", + "url": "https://github.com/mayf3/dsh-session-doctor", + "page": "https://awesome-dsh-plugin.com/p/mayf3/dsh-session-doctor/", + "category": "session", + "description": { + "en": "Diagnose, unstick, and read DSH sessions: list sessions with agent status, read conversations, diagnose stuck agents, recover them with cancel+keepInbox, and send messages to other sessions.", + "zh": "会话医生:列出会话(含 agent 运行状态)、读取会话记录、诊断卡死的 agent、解卡(cancel + keepInbox 保留排队消息)、向其他会话发送消息。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:mayf3/dsh-session-doctor", + "added": "2026-08-15" + }, + { + "name": "dsh-archive-manager", + "owner": "MichengAI", + "url": "https://github.com/MichengAI/dsh-archive-manager", + "page": "https://awesome-dsh-plugin.com/p/MichengAI/dsh-archive-manager/", + "category": "session", + "description": { + "en": "Adds an archived-sessions page in Settings to search, restore, and delete archived DeepSeek Harness sessions by workspace.", + "zh": "在设置里增加已归档会话页,可按工作区搜索、恢复和删除已归档会话。" + }, + "npm": "@michengai/dsh-archive-manager", + "stars": 20, + "downloads": 4031, + "install": "dsh plugin --profile web add @michengai/dsh-archive-manager", + "added": "2026-08-18" + }, + { + "name": "dsh-replay", + "owner": "MingoZhou", + "url": "https://github.com/MingoZhou/dsh-replay", + "page": "https://awesome-dsh-plugin.com/p/MingoZhou/dsh-replay/", + "category": "session", + "description": { + "en": "Replay sessions on a playable timeline with per-step token usage, audit sensitive operations, estimate cost, view fork lineage, compare sessions, and export standalone HTML replays.", + "zh": "在可播放的时间线上回放会话并显示逐步 token 用量,审计敏感操作,估算成本,查看 fork 血缘,对比会话,并可导出独立 HTML 回放。" + }, + "npm": "@mingozhou/dsh-replay", + "stars": 9, + "downloads": 571, + "install": "dsh plugin --profile web add @mingozhou/dsh-replay", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/MingoZhou/dsh-replay/main/assets/overview-light.png", + "https://raw.githubusercontent.com/MingoZhou/dsh-replay/main/assets/timeline-light.png", + "https://raw.githubusercontent.com/MingoZhou/dsh-replay/main/assets/audit-light.png" + ] + }, + { + "name": "dsh-message-edit", + "owner": "Moeblack", + "url": "https://github.com/Moeblack/dsh-message-edit", + "page": "https://awesome-dsh-plugin.com/p/Moeblack/dsh-message-edit/", + "category": "session", + "description": { + "en": "Branch-based message editing, reroll, retry, and a version timeline.", + "zh": "基于分支的消息编辑、reroll、重试与版本时间线。" + }, + "npm": "dsh-message-edit", + "stars": 43, + "downloads": 2125, + "install": "dsh plugin --profile web add dsh-message-edit", + "added": "2026-08-13" + }, + { + "name": "dsh-prompt-studio", + "owner": "Moeblack", + "url": "https://github.com/Moeblack/dsh-prompt-studio", + "page": "https://awesome-dsh-plugin.com/p/Moeblack/dsh-prompt-studio/", + "category": "session", + "description": { + "en": "Edit user and built-in system-prompt sections with live preview.", + "zh": "带实时预览的用户/内置 system prompt 分节编辑器。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:Moeblack/dsh-prompt-studio", + "added": "2026-08-13" + }, + { + "name": "dsh-archive-manager", + "owner": "MoonlitDropOfBlood", + "url": "https://github.com/MoonlitDropOfBlood/dsh-archive-manager", + "page": "https://awesome-dsh-plugin.com/p/MoonlitDropOfBlood/dsh-archive-manager/", + "category": "session", + "description": { + "en": "Archived-session manager for DeepSeek Harness: grouped archive panel with restore and delete.", + "zh": "DeepSeek Harness 会话归档管理:分组归档面板,支持恢复与删除。" + }, + "npm": "@duke-dsh-plugins/dsh-archive-manager", + "stars": 2, + "downloads": 536, + "install": "dsh plugin --profile web add @duke-dsh-plugins/dsh-archive-manager", + "added": "2026-08-23" + }, + { + "name": "dsh-session-pruner", + "owner": "mrzhangkris", + "url": "https://github.com/mrzhangkris/dsh-session-pruner", + "page": "https://awesome-dsh-plugin.com/p/mrzhangkris/dsh-session-pruner/", + "category": "session", + "description": { + "en": "Full-type session lifecycle management: one-shot subagents archived on completion, idle continuable subagents / main sessions archived (recoverable), a capacity cap, projection-cache cleanup, and a hot-reload settings panel.", + "zh": "全类型会话生命周期管理:一次性子 agent 完成后归档,闲置的可续聊子 agent 与主会话归档(可恢复),容量上限、投影缓存清理,以及热重载设置面板。" + }, + "npm": "dsh-session-pruner", + "stars": 2, + "downloads": 746, + "install": "dsh plugin --profile web add dsh-session-pruner", + "added": "2026-08-21" + }, + { + "name": "dsh-archived-sessions", + "owner": "MuWinds", + "url": "https://github.com/MuWinds/dsh-archived-sessions", + "page": "https://awesome-dsh-plugin.com/p/MuWinds/dsh-archived-sessions/", + "category": "session", + "description": { + "en": "Archived-session management: browse archived sessions, unarchive them, or clear them out.", + "zh": "归档会话管理:浏览已归档会话,支持恢复(取消归档)与清空。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:MuWinds/dsh-archived-sessions", + "added": "2026-08-15" + }, + { + "name": "dsh-session-drafts", + "owner": "ne-ilyxa", + "url": "https://github.com/ne-ilyxa/dsh-session-drafts", + "page": "https://awesome-dsh-plugin.com/p/ne-ilyxa/dsh-session-drafts/", + "category": "session", + "description": { + "en": "Cursor-style New Session: every click creates a fresh blank draft session (persisted before the first message) instead of reusing the workspace's single one, plus a sidebar Drafts switcher to keep several empty chats open at once.", + "zh": "Cursor 风格的新会话:每次点击都新建一个空白草稿会话(首条消息前即持久化),不再复用工作区唯一的空会话;侧栏新增草稿切换器,可同时保留多个空会话。" + }, + "npm": "@ne-ilyxa/dsh-session-drafts", + "stars": 2, + "downloads": 298, + "install": "dsh plugin --profile web add @ne-ilyxa/dsh-session-drafts", + "added": "2026-08-23", + "screenshots": [ + "https://raw.githubusercontent.com/ne-ilyxa/dsh-session-drafts/master/assets/drafts-popover.png", + "https://raw.githubusercontent.com/ne-ilyxa/dsh-session-drafts/master/assets/drafts-switched.png" + ] + }, + { + "name": "dsh-task-control", + "owner": "ningmengxr", + "url": "https://github.com/ningmengxr/dsh-task-control", + "page": "https://awesome-dsh-plugin.com/p/ningmengxr/dsh-task-control/", + "category": "session", + "description": { + "en": "Task control for DeepSeek Harness: one-click emergency stop and force-termination of stuck downloads and tool calls, download progress, stuck detection, pwsh activity monitoring, and append-condition / pause / resume with invisible recovery.", + "zh": "DeepSeek Harness 任务控制:卡死的下载与工具调用一键急停与强制终止、下载进度、卡死识别、pwsh 运行状态检测,以及追加条件/暂停/恢复(恢复指令隐形注入,不打扰聊天)。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:ningmengxr/dsh-task-control", + "added": "2026-08-27" + }, + { + "name": "review-quote-sh", + "owner": "Nothree-code", + "url": "https://github.com/Nothree-code/review-quote-sh", + "page": "https://awesome-dsh-plugin.com/p/Nothree-code/review-quote-sh/", + "category": "session", + "description": { + "en": "Message review and quote chips for conversations: multi-model cross review (code/reply) with severity grading and summaries, review history, and host-persisted preferences.", + "zh": "对话消息审查与引用:多模型并行互审(代码/回复审查、严重度分级、综合汇总)、引用问答胶囊、评审历史与偏好记忆。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Nothree-code/review-quote-sh", + "added": "2026-08-15" + }, + { + "name": "dsh-chat-import", + "owner": "Nwflower", + "url": "https://github.com/Nwflower/dsh-chat-import", + "page": "https://awesome-dsh-plugin.com/p/Nwflower/dsh-chat-import/", + "category": "session", + "description": { + "en": "Import full-fidelity chat histories from 13 coding agents (Claude Code, Codex, ChatGPT, Cursor, Gemini, opencode, and more) as resumable DeepSeek Harness sessions, with reverse export back to Claude Code.", + "zh": "把 13 家 coding agent(Claude Code、Codex、ChatGPT、Cursor、Gemini、opencode 等)的完整对话历史导入为可续聊的 DeepSeek Harness 会话,并支持反向导出回 Claude Code。" + }, + "npm": "dsh-chat-import", + "stars": 109, + "downloads": 9540, + "install": "dsh plugin --profile web add dsh-chat-import", + "added": "2026-08-13" + }, + { + "name": "dsh-file-claim", + "owner": "Nwflower", + "url": "https://github.com/Nwflower/dsh-file-claim", + "page": "https://awesome-dsh-plugin.com/p/Nwflower/dsh-file-claim/", + "category": "session", + "description": { + "en": "File claim/release protection for parallel DSH sessions on the same workspace (heartbeat stale takeover, pending 3-way merge area).", + "zh": "同一工作区并行多会话的文件认领与写入保护(claim/release、心跳 stale 接管、pending 三路合并)。" + }, + "npm": "dsh-file-claim", + "stars": 6, + "downloads": 465, + "install": "dsh plugin --profile web add dsh-file-claim", + "added": "2026-08-14" + }, + { + "name": "dsh-task-control", + "owner": "p2coder", + "url": "https://github.com/p2coder/dsh-task-control", + "page": "https://awesome-dsh-plugin.com/p/p2coder/dsh-task-control/", + "category": "session", + "description": { + "en": "Pause / resume / cancel the in-flight conversation task from the composer area: forced pause interrupts immediately and remembers the interrupted tool, safe pause lands after the tool or reasoning finishes, resume asks for confirmation, and the default pause granularity is configurable in the settings.", + "zh": "在输入区暂停/恢复/取消正在运行的对话任务:强制暂停立即中断并记住被中断的工具,安全暂停等工具/推理完成后再落地,恢复时需确认;默认暂停粒度可在设置中配置(出厂 safe wait)。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:p2coder/dsh-task-control", + "added": "2026-08-16" + }, + { + "name": "dsh-bookmarks", + "owner": "penguin-oo", + "url": "https://github.com/penguin-oo/dsh-bookmarks", + "page": "https://awesome-dsh-plugin.com/p/penguin-oo/dsh-bookmarks/", + "category": "session", + "description": { + "en": "Bookmark assistant replies with notes and tags; browse every bookmark in one cross-session center and export to Markdown.", + "zh": "收藏 AI 回复(备注/标签),跨会话收藏中心(搜索/筛选/跳回会话),一键导出 Markdown。" + }, + "npm": "dsh-bookmarks", + "stars": 12, + "downloads": 792, + "install": "dsh plugin --profile web add dsh-bookmarks", + "added": "2026-08-14" + }, + { + "name": "dsh-claude-move", + "owner": "PerryLink", + "url": "https://github.com/PerryLink/dsh-claude-move", + "page": "https://awesome-dsh-plugin.com/p/PerryLink/dsh-claude-move/", + "category": "session", + "description": { + "en": "Four-source migration wizard: move Claude Code, Codex, OpenCode and Hermes sessions, memories, skills, instructions and slash commands into DSH (/move wizard with approval gate and idempotent move.json, resumable sessions).", + "zh": "四合一迁移向导:把 Claude Code、Codex、OpenCode、Hermes 的会话、记忆、技能、指令与斜杠命令迁入 DSH(/move 向导,审批门 + 幂等 move.json,会话可续聊)。" + }, + "npm": "dsh-claude-move", + "stars": 10, + "downloads": 1302, + "install": "dsh plugin --profile web add dsh-claude-move", + "added": "2026-08-16" + }, + { + "name": "dsh-session-pin", + "owner": "PerryLink", + "url": "https://github.com/PerryLink/dsh-session-pin", + "page": "https://awesome-dsh-plugin.com/p/PerryLink/dsh-session-pin/", + "category": "session", + "description": { + "en": "Pin sessions and workspaces to the top of the Web sidebar with per-pin row colors, a header toggle and a pinned panel; 0.4.0 adds a navigation organizer — pin groups (boards), tags and saved filter views, session health summaries, and /goto.", + "zh": "把会话与工作区置顶到 Web 侧边栏顶部:行级图钉与换色、会话头开关、已置顶面板;0.4.0 再加导航组织器——Pin 分组(boards)、标签与保存视图、会话健康摘要与 /goto。" + }, + "npm": "dsh-session-pin", + "stars": 3, + "downloads": 1483, + "install": "dsh plugin --profile web add dsh-session-pin", + "added": "2026-08-16" + }, + { + "name": "dsh-workspace-mover", + "owner": "PianoPrince", + "url": "https://github.com/PianoPrince/dsh-workspace-mover", + "page": "https://awesome-dsh-plugin.com/p/PianoPrince/dsh-workspace-mover/", + "category": "session", + "description": { + "en": "Move sessions across workspaces by drag-and-drop with true in-place migration; recover orphaned or unregistered sessions; re-point stale workspaces after folder renames. IDs and history stay intact, with per-step backup and rollback.", + "zh": "拖拽跨工作区真迁移会话,找回失联与未记账会话,文件夹改名后一键原地重定向工作区;ID 与历史原样保留,步步备份回滚。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:PianoPrince/dsh-workspace-mover", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/PianoPrince/dsh-workspace-mover/HEAD/docs/media/drag_session_to_another_workspace.png", + "https://raw.githubusercontent.com/PianoPrince/dsh-workspace-mover/HEAD/docs/media/confirm_popup.png", + "https://raw.githubusercontent.com/PianoPrince/dsh-workspace-mover/HEAD/docs/media/setting_dialogue_repair.png", + "https://raw.githubusercontent.com/PianoPrince/dsh-workspace-mover/HEAD/docs/media/workspace_examination.png", + "https://raw.githubusercontent.com/PianoPrince/dsh-workspace-mover/HEAD/docs/media/remove_popup.png" + ] + }, + { + "name": "dsh-session-link", + "owner": "PwnKY", + "url": "https://github.com/PwnKY/dsh-session-link", + "page": "https://awesome-dsh-plugin.com/p/PwnKY/dsh-session-link/", + "category": "session", + "description": { + "en": "Copy and open `dsh://` session deep links, or paste them into another conversation to inject a bounded, read-only snapshot of the referenced session.", + "zh": "复制、打开 `dsh://` 会话深链,或将其粘贴到另一对话中,注入被引用会话的受限只读快照。" + }, + "npm": "dsh-session-link", + "stars": 5, + "downloads": 254, + "install": "dsh plugin --profile web add dsh-session-link", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/PwnKY/dsh-session-link/master/assets/screenshots/01-cross-session-context.png", + "https://raw.githubusercontent.com/PwnKY/dsh-session-link/master/assets/screenshots/02-source-session.png" + ] + }, + { + "name": "dsh-plugin-atlas", + "owner": "qinyre", + "url": "https://github.com/qinyre/dsh-plugin-atlas", + "page": "https://awesome-dsh-plugin.com/p/qinyre/dsh-plugin-atlas/", + "category": "session", + "description": { + "en": "Settings section for archived sessions: browse by workspace, search, batch unarchive with the sidebar updating live, and optional auto-archive rules (idle days, keep-last-N per workspace) with a dry-run preview — plus a left-edge turn rail for jumping between turns of long conversations and ↑/↓ input history in the composer.", + "zh": "设置页的归档会话分区:按工作区分组浏览、搜索、批量取消归档,侧边栏实时恢复;自动归档规则(不活跃天数、每工作区保留条数)可选,执行前可试运行预览。另含长会话的左缘轮次刻度尺(悬停预览、点击跳转、Alt+↑/↓ 逐轮移动)与输入框 ↑/↓ 历史翻找。" + }, + "npm": "dsh-plugin-atlas", + "stars": 2, + "downloads": 1199, + "install": "dsh plugin --profile web add dsh-plugin-atlas", + "added": "2026-08-24", + "screenshots": [ + "https://raw.githubusercontent.com/qinyre/dsh-plugin-atlas/main/docs/images/screenshot-rail.png", + "https://raw.githubusercontent.com/qinyre/dsh-plugin-atlas/main/docs/images/screenshot-archive.png" + ] + }, + { + "name": "dsh-client-ui-session-mention", + "owner": "qiyu166", + "url": "https://github.com/qiyu166/dsh-client-ui-session-mention", + "page": "https://awesome-dsh-plugin.com/p/qiyu166/dsh-client-ui-session-mention/", + "category": "session", + "description": { + "en": "Type @ in the composer to reference another conversation: pick a historical session and inject its compacted context into the current prompt.", + "zh": "输入 @ 引用另一个对话:选择历史会话,将其压缩总结注入当前提示词。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:qiyu166/dsh-client-ui-session-mention", + "added": "2026-08-24" + }, + { + "name": "dsh-session-move", + "owner": "reinocheong", + "url": "https://github.com/reinocheong/dsh-session-move", + "page": "https://awesome-dsh-plugin.com/p/reinocheong/dsh-session-move/", + "category": "session", + "description": { + "en": "Move a session to another folder from the Web UI via drag & drop or a menu picker, permanently delete a session with a risk-consent dialog, and AI-rename a session by summarizing the whole conversation (typo-aware). Each action also ships as an agent tool.", + "zh": "在 Web 侧边栏把会话移动到别的文件夹(拖拽或菜单选择),带风险确认地永久删除会话,以及 AI 重命名会话(总结整个对话并自动纠正错别字);每个操作都提供 agent 工具。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:reinocheong/dsh-session-move", + "added": "2026-08-15" + }, + { + "name": "dsh-brief-session-title", + "owner": "Relethe", + "url": "https://github.com/Relethe/dsh-brief-session-title", + "page": "https://awesome-dsh-plugin.com/p/Relethe/dsh-brief-session-title/", + "category": "session", + "description": { + "en": "Condenses DSH session titles from a full sentence into a short verb-free phrase, aiming for under five words.", + "zh": "把 DSH 会话标题从一句话精简为不含动词的短语,目标五个词以内。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Relethe/dsh-brief-session-title", + "added": "2026-08-20" + }, + { + "name": "DSH-EasyRewrite", + "owner": "Renzic-Stone", + "url": "https://github.com/Renzic-Stone/DSH-EasyRewrite", + "page": "https://awesome-dsh-plugin.com/p/Renzic-Stone/DSH-EasyRewrite/", + "category": "session", + "description": { + "en": "Inline-edit and recall your own user messages in dsh web — lazily and seamlessly, with a version pager and per-session draft persistence.", + "zh": "在 dsh web 中内联编辑与撤回自己的消息——惰性、无痕,带版本翻页器与会话级草稿持久化。" + }, + "npm": "dsh-easyrewrite", + "stars": 78, + "downloads": 4282, + "install": "dsh plugin --profile web add dsh-easyrewrite", + "added": "2026-08-19" + }, + { + "name": "dsh-off-peak-message#dsh-offpeak-send", + "owner": "Ricketts-Guo", + "url": "https://github.com/Ricketts-Guo/dsh-off-peak-message/tree/main/dsh-offpeak-send", + "page": "https://awesome-dsh-plugin.com/p/Ricketts-Guo/dsh-off-peak-message--dsh-offpeak-send/", + "category": "session", + "description": { + "en": "Off-peak message scheduler: turn on valley mode and the composer queues your messages until DeepSeek off-peak hours (half price), then auto-injects them into the session and gets replies. Native-look composer, hotkeys (Enter queue / ⌘+Enter send now / Esc exit), per-session toggle persisted across restarts.", + "zh": "谷时发送调度器:开启谷时模式后输入框消息排队至 DeepSeek 谷时窗口(半价)自动注入会话并得到回复。原生外观输入条、快捷键(Enter 入队 / ⌘+Enter 立即发送 / Esc 退出)、按会话记忆的开关,重启不丢。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Ricketts-Guo/dsh-off-peak-message#path:/dsh-offpeak-send", + "added": "2026-08-21" + }, + { + "name": "dsh-codex-pins", + "owner": "robbyisrobby", + "url": "https://github.com/robbyisrobby/dsh-codex-pins", + "page": "https://awesome-dsh-plugin.com/p/robbyisrobby/dsh-codex-pins/", + "category": "session", + "description": { + "en": "Splits the DeepSeek Harness sidebar into independently scrollable Pinned and Recents panes, and hides pinned sessions from Recents.", + "zh": "把 DeepSeek Harness 侧边栏分成可独立滚动的置顶栏和最近栏,置顶过的会话不再出现在最近列表里。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:robbyisrobby/dsh-codex-pins", + "added": "2026-08-24" + }, + { + "name": "dsh-message-fold", + "owner": "RyensX", + "url": "https://github.com/RyensX/dsh-message-fold", + "page": "https://awesome-dsh-plugin.com/p/RyensX/dsh-message-fold/", + "category": "session", + "description": { + "en": "Provides Codex-style session message folding for DeepSeek Harness.", + "zh": "为 DeepSeek Harness 提供 Codex 风格的会话消息折叠。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:RyensX/dsh-message-fold", + "added": "2026-08-21" + }, + { + "name": "dsh-prompt-manager", + "owner": "SaiSenBox", + "url": "https://github.com/SaiSenBox/dsh-prompt-manager", + "page": "https://awesome-dsh-plugin.com/p/SaiSenBox/dsh-prompt-manager/", + "category": "session", + "description": { + "en": "Browser-local prompt library with a composer picker for injecting multiple session-scoped system prompts, branch inheritance, bilingual UI, favorites, and JSON backups.", + "zh": "浏览器本地提示词库:在输入框选择器中同时注入多条会话级系统提示词,支持分支继承、中英界面、收藏与 JSON 备份。" + }, + "npm": "dsh-prompt-manager", + "stars": 5, + "downloads": 477, + "install": "dsh plugin --profile web add dsh-prompt-manager", + "added": "2026-08-15" + }, + { + "name": "harness-session-delete", + "owner": "Seetraum", + "url": "https://github.com/Seetraum/harness-session-delete", + "page": "https://awesome-dsh-plugin.com/p/Seetraum/harness-session-delete/", + "category": "session", + "description": { + "en": "Recycle bin for DSH sessions: sidebar delete icons, multi-select restore and permanent delete, live-session protection.", + "zh": "DSH 会话回收站:侧边栏删除图标、多选还原与彻底删除、活跃会话保护。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Seetraum/harness-session-delete", + "added": "2026-08-27" + }, + { + "name": "dsh-session-manager", + "owner": "Semidia", + "url": "https://github.com/Semidia/dsh-session-manager", + "page": "https://awesome-dsh-plugin.com/p/Semidia/dsh-session-manager/", + "category": "session", + "description": { + "en": "Right-click session menu and sidebar session manager: pin, rename, archive, fork, export, copy cwd/id/deep link, and open in explorer or a new window.", + "zh": "会话行右键菜单与侧边栏会话管理:置顶、重命名、归档、分叉、导出、复制工作目录/会话 ID/深链,在资源管理器或新窗口打开。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:Semidia/dsh-session-manager", + "added": "2026-08-15" + }, + { + "name": "AgentSideCar#plugin", + "owner": "shendeguize", + "url": "https://github.com/shendeguize/AgentSideCar/tree/main/plugin", + "page": "https://awesome-dsh-plugin.com/p/shendeguize/AgentSideCar--plugin/", + "category": "session", + "description": { + "en": "Cross-agent monitoring board for local AI agent sessions (cursor-ide, claude, codex, copilot, kimi and dsh itself) with session timelines, and message injection behind a default-off two-phase confirmation gate.", + "zh": "跨 agent 看板监控本机 AI agent 会话(cursor-ide、claude、codex、copilot、kimi 及 dsh 自身),提供会话时间线,并可经默认关闭的两阶段确认门注入消息。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:shendeguize/AgentSideCar#path:/plugin", + "added": "2026-08-26" + }, + { + "name": "dsh-rewind", + "owner": "SiriLee", + "url": "https://github.com/SiriLee/dsh-rewind", + "page": "https://awesome-dsh-plugin.com/p/SiriLee/dsh-rewind/", + "category": "session", + "description": { + "en": "In-place conversation rewind in the same session window without forking (Claude Code /rewind semantics): a per-message ↶ button cuts the model context back to any user message, with an optional Claude-Code-style file restore from disk-persisted before-backups.", + "zh": "同一会话窗口内的 in-place 对话回退(不 fork,Claude Code /rewind 语义):每条消息的 ↶ 按钮把模型上下文回退到任意用户消息,可选用磁盘持久化的 before-backups 还原工作区文件。" + }, + "npm": "dsh-rewind-plugin", + "stars": 16, + "downloads": 5256, + "install": "dsh plugin --profile web add dsh-rewind-plugin", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/SiriLee/dsh-rewind/HEAD/assets/screenshots/rewind-button.png", + "https://raw.githubusercontent.com/SiriLee/dsh-rewind/HEAD/assets/screenshots/mode-popover.png", + "https://raw.githubusercontent.com/SiriLee/dsh-rewind/HEAD/assets/screenshots/impact-list.png", + "https://raw.githubusercontent.com/SiriLee/dsh-rewind/HEAD/assets/screenshots/rewind-candidates.png" + ] + }, + { + "name": "dsh-what-changed", + "owner": "sjh9714", + "url": "https://github.com/sjh9714/dsh-what-changed", + "page": "https://awesome-dsh-plugin.com/p/sjh9714/dsh-what-changed/", + "category": "session", + "description": { + "en": "Reviews every file the agent wrote in a session from the session header, with per-file hunks and refused writes counted apart from changes.", + "zh": "在会话顶栏审阅本次会话 Agent 写过的所有文件,按文件列出逐处改动,被权限拒绝的写入单独计数不算改动。" + }, + "npm": "dsh-what-changed", + "stars": 2, + "downloads": 1175, + "install": "dsh plugin --profile web add dsh-what-changed", + "added": "2026-08-18" + }, + { + "name": "dsh-task-notice-board", + "owner": "SLin-code", + "url": "https://github.com/SLin-code/dsh-task-notice-board", + "page": "https://awesome-dsh-plugin.com/p/SLin-code/dsh-task-notice-board/", + "category": "session", + "description": { + "en": "Task-scoped Workspace to Task to Session control center for DSH Web, with Session boards, archived conversation viewing, and bounded long-term memory shared across linked Sessions.", + "zh": "面向 DSH Web 的任务级 Workspace 到 Task 到 Session 协作控制台,提供 Session 看板、归档对话查看,以及在关联 Session 之间共享的有界长期记忆。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:SLin-code/dsh-task-notice-board", + "added": "2026-08-21" + }, + { + "name": "dsh-session-handoff", + "owner": "snow-The", + "url": "https://github.com/snow-The/dsh-session-handoff", + "page": "https://awesome-dsh-plugin.com/p/snow-The/dsh-session-handoff/", + "category": "session", + "description": { + "en": "Session handoff & context management: structured handoff documents (export/resume/status) so a fresh session can continue seamlessly, plus active context pruning (acp_* tools via the official compaction API with model-authored summaries).", + "zh": "会话交接与上下文管理:导出/恢复结构化交接文档,让新会话无缝续接;并集成了主动上下文裁剪(acp_* 工具,基于官方 compaction API 与模型自写摘要)。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:snow-The/dsh-session-handoff", + "added": "2026-08-20" + }, + { + "name": "dsh-query-jump", + "owner": "SocFeng", + "url": "https://github.com/SocFeng/dsh-query-jump", + "page": "https://awesome-dsh-plugin.com/p/SocFeng/dsh-query-jump/", + "category": "session", + "description": { + "en": "DSH WebUI session query navigation with a faint conversation-edge rail, a hover list of real user queries, and click-to-jump back to the matching bubble.", + "zh": "DSH WebUI 会话提问导航:对话区右缘淡色短横线,悬停列出真实用户提问,点击跳回对应气泡。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:SocFeng/dsh-query-jump", + "added": "2026-08-18" + }, + { + "name": "dsh-plugin-message-edit", + "owner": "SpookySandwich", + "url": "https://github.com/SpookySandwich/dsh-plugin-message-edit", + "page": "https://awesome-dsh-plugin.com/p/SpookySandwich/dsh-plugin-message-edit/", + "category": "session", + "description": { + "en": "Edit a sent message to rewind and branch the conversation from that turn, switch versions with a counter under the bubble, and browse every branch in a turn-level version tree.", + "zh": "编辑已发送的消息即可从该轮次回溯并分叉对话,用气泡下方的计数器切换版本,并在轮次级版本树中浏览所有分支。" + }, + "npm": "dsh-plugin-message-edit", + "stars": 3, + "downloads": 209, + "install": "dsh plugin --profile web add dsh-plugin-message-edit", + "added": "2026-08-23" + }, + { + "name": "dsh-plugin-no-workspace", + "owner": "SpookySandwich", + "url": "https://github.com/SpookySandwich/dsh-plugin-no-workspace", + "page": "https://awesome-dsh-plugin.com/p/SpookySandwich/dsh-plugin-no-workspace/", + "category": "session", + "description": { + "en": "Start and detach standalone chats without a mandatory workspace, keeping DSH's native workspace UI intact.", + "zh": "无需强制绑定工作区即可新建和分离独立会话,同时保留 DSH 原生的工作区界面。" + }, + "npm": "dsh-plugin-no-workspace", + "stars": 1, + "downloads": 166, + "install": "dsh plugin --profile web add dsh-plugin-no-workspace", + "added": "2026-08-24" + }, + { + "name": "dsh-queue-plus", + "owner": "starslittle", + "url": "https://github.com/starslittle/dsh-queue-plus", + "page": "https://awesome-dsh-plugin.com/p/starslittle/dsh-queue-plus/", + "category": "session", + "description": { + "en": "A simple DSH queue panel for editing, removing, steering, reordering, and bulk removal.", + "zh": "DSH 排队消息增强面板,支持编辑、删除、插话、排序和批量删除。" + }, + "npm": "dsh-queue-plus", + "stars": 17, + "downloads": 402, + "install": "dsh plugin --profile web add dsh-queue-plus", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/starslittle/dsh-queue-plus/main/assets/queue-default.png", + "https://raw.githubusercontent.com/starslittle/dsh-queue-plus/main/assets/queue-remove-all.png", + "https://raw.githubusercontent.com/starslittle/dsh-queue-plus/main/assets/queue-remove-complete.png", + "https://raw.githubusercontent.com/starslittle/dsh-queue-plus/main/assets/queue-sort.png" + ] + }, + { + "name": "dsh-linked-folders", + "owner": "Sttrevens", + "url": "https://github.com/Sttrevens/dsh-linked-folders", + "page": "https://awesome-dsh-plugin.com/p/Sttrevens/dsh-linked-folders/", + "category": "session", + "description": { + "en": "Multi-folder workspace: a durable global linked-folders list plus per-session on-the-fly linking (link_folder/unlink_folder), managed from the Web sidebar with a native folder picker.", + "zh": "多文件夹工作区:全局链接文件夹列表 + 会话内临时挂载(link_folder/unlink_folder),侧边栏管理并支持系统选目录。" + }, + "npm": "@steven-wu/dsh-linked-folders", + "stars": 1, + "downloads": 914, + "install": "dsh plugin --profile web add @steven-wu/dsh-linked-folders", + "added": "2026-08-17" + }, + { + "name": "shl-session-history", + "owner": "sunyuhuirong", + "url": "https://github.com/sunyuhuirong/shl-session-history", + "page": "https://awesome-dsh-plugin.com/p/sunyuhuirong/shl-session-history/", + "category": "session", + "description": { + "en": "Conversation history rail on the left edge of the chat: each tick is one real user request, hover to preview its text, click to jump to that turn.", + "zh": "会话历史迷你滑轨:对话区左侧垂直居中的短横线,每条横线代表一条真实用户请求,悬停预览请求内容,点击跳转到对应轮次。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:sunyuhuirong/shl-session-history", + "added": "2026-08-23" + }, + { + "name": "dsh-checkpoint-diff", + "owner": "tmpdot", + "url": "https://github.com/tmpdot/dsh-checkpoint-diff", + "page": "https://awesome-dsh-plugin.com/p/tmpdot/dsh-checkpoint-diff/", + "category": "session", + "description": { + "en": "Browses checkpoints produced by dsh-checkpoint-rewind as a timeline of time nodes and shows per-file line diffs between any two of them, with trace replay from session logs (no snapshot producer needed) and preview-first workspace rollback with single-shot undo, via /diff and /rollback commands, a JSON HTTP API, and a GUI panel.", + "zh": "把 dsh-checkpoint-rewind 产生的检查点变成时间线时间节点,任意两节点之间逐文件行级 diff;可从会话日志重放 trace 区间差异(无需快照生产者);支持预览式工作区回滚与一次性撤销。提供 /diff、/rollback 命令、JSON HTTP API 与 GUI 面板。" + }, + "npm": "dsh-checkpoint-diff", + "stars": 2, + "downloads": 2007, + "install": "dsh plugin --profile web add dsh-checkpoint-diff", + "added": "2026-08-20" + }, + { + "name": "dsh-sessions-manager", + "owner": "TOBYCAI", + "url": "https://github.com/TOBYCAI/dsh-sessions-manager", + "page": "https://awesome-dsh-plugin.com/p/TOBYCAI/dsh-sessions-manager/", + "category": "session", + "description": { + "en": "Archive, restore, permanently delete, and move DeepSeek Harness sessions across workspaces from one unified settings panel.", + "zh": "在统一设置面板中归档、恢复、彻底删除并跨工作区移动 DeepSeek Harness 会话。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:TOBYCAI/dsh-sessions-manager", + "added": "2026-08-25" + }, + { + "name": "dsh-plugin-bridge", + "owner": "Totoro-qaq", + "url": "https://github.com/Totoro-qaq/dsh-plugin-bridge", + "page": "https://awesome-dsh-plugin.com/p/Totoro-qaq/dsh-plugin-bridge/", + "category": "session", + "description": { + "en": "Moves an existing DSH session to another agent preset through a previewable five-section handoff, preserving the source session and either pausing the target for confirmation or continuing immediately.", + "zh": "通过可预览的五段式交接,将已有 DSH 会话迁移到另一个 Agent Preset;保留源会话,并可让目标会话暂停等待确认或立即继续。" + }, + "npm": "dsh-plugin-bridge", + "stars": 134, + "downloads": 311, + "install": "dsh plugin --profile web add dsh-plugin-bridge", + "added": "2026-08-21", + "screenshots": [ + "https://raw.githubusercontent.com/Totoro-qaq/dsh-plugin-bridge/main/assets/cover/cover-en.png", + "https://raw.githubusercontent.com/Totoro-qaq/dsh-plugin-bridge/main/assets/bridge-demo.en.gif" + ] + }, + { + "name": "dsh-set-workspace", + "owner": "tsingshitao-nuke", + "url": "https://github.com/tsingshitao-nuke/dsh-set-workspace", + "page": "https://awesome-dsh-plugin.com/p/tsingshitao-nuke/dsh-set-workspace/", + "category": "session", + "description": { + "en": "Adds a Windows Explorer right-click action to open a folder as a DSH workspace, launching DSH if needed.", + "zh": "给 Windows 文件管理器加右键项:把文件夹设为 DSH 工作区并切换到它,DSH 未运行会自动拉起。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:tsingshitao-nuke/dsh-set-workspace", + "added": "2026-08-21" + }, + { + "name": "dsh-session-nav", + "owner": "tuogusa", + "url": "https://github.com/tuogusa/dsh-session-nav", + "page": "https://awesome-dsh-plugin.com/p/tuogusa/dsh-session-nav/", + "category": "session", + "description": { + "en": "A floating button beside the chat that opens a searchable list of all questions in the session for quick jumps, with smooth hover-open/close behavior.", + "zh": "对话旁的悬浮按钮,打开当前会话全部提问的可搜索列表并快速跳转;悬浮弹出/移出平滑关闭。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:tuogusa/dsh-session-nav", + "added": "2026-08-15" + }, + { + "name": "dsh-session-tags", + "owner": "tuogusa", + "url": "https://github.com/tuogusa/dsh-session-tags", + "page": "https://awesome-dsh-plugin.com/p/tuogusa/dsh-session-tags/", + "category": "session", + "description": { + "en": "Attach tags to sessions and search sessions by tag in the Web settings panel.", + "zh": "为会话添加标签,并在 Web 设置面板中按标签搜索会话。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:tuogusa/dsh-session-tags", + "added": "2026-08-15" + }, + { + "name": "dsh-session-delete", + "owner": "txlznbzsdj-collab", + "url": "https://github.com/txlznbzsdj-collab/dsh-session-delete", + "page": "https://awesome-dsh-plugin.com/p/txlznbzsdj-collab/dsh-session-delete/", + "category": "session", + "description": { + "en": "Adds a \"Delete\" item to the sidebar session menu with a confirmation dialog, then hard-deletes the session.", + "zh": "在侧边栏会话菜单添加「删除」项,经二次确认后硬删除该会话。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:txlznbzsdj-collab/dsh-session-delete", + "added": "2026-08-19" + }, + { + "name": "dsh-archived-chats", + "owner": "Ultronen", + "url": "https://github.com/Ultronen/dsh-archived-chats", + "page": "https://awesome-dsh-plugin.com/p/Ultronen/dsh-archived-chats/", + "category": "session", + "description": { + "en": "Session Archive for DeepSeek Harness groups archived chats by workspace with full-text search and native read-only preview, keeps validated History versions for restore-as-copy, and adds tags, notes, ZIP backup and restore, a snapshot-protected Recycle Bin, storage accounting, preview-first retention, and read-only session lineage; all data stays local.", + "zh": "DeepSeek Harness 会话档案:按工作区浏览和全文搜索归档聊天,提供原生只读预览、可恢复为副本的历史版本、标签备注、ZIP 备份恢复、带保护快照的回收站、空间分账、预览优先的保留策略和只读会话血缘;所有数据留在本机。" + }, + "npm": "dsh-archived-chats", + "stars": 11, + "downloads": 2073, + "install": "dsh plugin --profile web add dsh-archived-chats", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/Ultronen/dsh-archived-chats/HEAD/assets/screenshots/preview-01.png", + "https://raw.githubusercontent.com/Ultronen/dsh-archived-chats/HEAD/assets/screenshots/preview-02.png", + "https://raw.githubusercontent.com/Ultronen/dsh-archived-chats/HEAD/assets/screenshots/preview-03.png", + "https://raw.githubusercontent.com/Ultronen/dsh-archived-chats/HEAD/assets/screenshots/preview-04.png", + "https://raw.githubusercontent.com/Ultronen/dsh-archived-chats/HEAD/assets/screenshots/preview-05.png", + "https://raw.githubusercontent.com/Ultronen/dsh-archived-chats/HEAD/assets/screenshots/preview-06.png", + "https://raw.githubusercontent.com/Ultronen/dsh-archived-chats/HEAD/assets/screenshots/preview-07.png", + "https://raw.githubusercontent.com/Ultronen/dsh-archived-chats/HEAD/assets/screenshots/preview-08.png" + ] + }, + { + "name": "dsh-projects", + "owner": "WenhongPan", + "url": "https://github.com/WenhongPan/dsh-projects", + "page": "https://awesome-dsh-plugin.com/p/WenhongPan/dsh-projects/", + "category": "session", + "description": { + "en": "Project and session organization with multi-folder groups, attention summaries, grouped chat search, native folder picking, pinning, favorites, ordering, and an archive center.", + "zh": "项目与会话整理工具:支持多文件夹组合、待处理状态摘要、分组聊天搜索、原生目录选择、置顶、收藏、排序和归档中心。" + }, + "npm": null, + "tarball": "https://github.com/WenhongPan/dsh-projects/releases/download/v0.3.0-alpha.1/dsh-projects-0.3.0-alpha.1.tgz", + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/WenhongPan/dsh-projects/releases/download/v0.3.0-alpha.1/dsh-projects-0.3.0-alpha.1.tgz\"", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/WenhongPan/dsh-projects/HEAD/docs/assets/picker.webp", + "https://raw.githubusercontent.com/WenhongPan/dsh-projects/HEAD/docs/assets/create.webp", + "https://raw.githubusercontent.com/WenhongPan/dsh-projects/HEAD/docs/assets/native.webp" + ] + }, + { + "name": "dsh-suite#plugin-session-export", + "owner": "whyihaveyou", + "url": "https://github.com/whyihaveyou/dsh-suite/tree/main/packages/plugins/plugin-session-export", + "page": "https://awesome-dsh-plugin.com/p/whyihaveyou/dsh-suite--packages-plugins-plugin-session-export/", + "category": "session", + "description": { + "en": "Export the append-only session log as human-readable Markdown or HTML, grouped by trajectory source.", + "zh": "把 append-only 会话日志导出为按轨迹来源分组的可读 Markdown 或 HTML。" + }, + "npm": null, + "stars": 48, + "downloads": null, + "install": "dsh plugin --profile web add github:whyihaveyou/dsh-suite#path:/packages/plugins/plugin-session-export", + "added": "2026-08-14" + }, + { + "name": "dsh-prompt-stash", + "owner": "Wine-Red", + "url": "https://github.com/Wine-Red/dsh-prompt-stash", + "page": "https://awesome-dsh-plugin.com/p/Wine-Red/dsh-prompt-stash/", + "category": "session", + "description": { + "en": "Local, per-session LIFO prompt stash for temporarily setting aside unfinished composer text and safely restoring it later.", + "zh": "本地、按会话隔离的 LIFO 输入暂存:临时收起未完成的输入,之后安全恢复并继续编辑。" + }, + "npm": "dsh-prompt-stash", + "stars": 3, + "downloads": 845, + "install": "dsh plugin --profile web add dsh-prompt-stash", + "added": "2026-08-14" + }, + { + "name": "dsh-plugin-hub#packages/dsh-idle-archive", + "owner": "wingsky-1", + "url": "https://github.com/wingsky-1/dsh-plugin-hub/tree/main/packages/dsh-idle-archive", + "page": "https://awesome-dsh-plugin.com/p/wingsky-1/dsh-plugin-hub--packages-dsh-idle-archive/", + "category": "session", + "description": { + "en": "Idle session reminder: prompts to archive sessions untouched for N hours, with a snooze window after each refusal, using the official archiveSession API.", + "zh": "会话闲置提醒:超过 N 小时未对话的会话弹窗询问是否归档,拒绝后进入静默期;归档走官方 archiveSession 接口。" + }, + "npm": "@wingsky-1/dsh-idle-archive", + "stars": 5, + "downloads": 1764, + "install": "dsh plugin --profile web add @wingsky-1/dsh-idle-archive", + "added": "2026-08-18" + }, + { + "name": "deepseek-web-import", + "owner": "wpc0323", + "url": "https://github.com/wpc0323/deepseek-web-import", + "page": "https://awesome-dsh-plugin.com/p/wpc0323/deepseek-web-import/", + "category": "session", + "description": { + "en": "Imports chat.deepseek.com web conversations into DeepSeek Harness as resumable sessions, from a settings-page UI.", + "zh": "从设置页把 chat.deepseek.com 网页端对话导入为 DeepSeek Harness 会话,导入后可在其中继续对话。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:wpc0323/deepseek-web-import", + "added": "2026-08-18" + }, + { + "name": "dsh-chat-manager", + "owner": "WSL043", + "url": "https://github.com/WSL043/dsh-chat-manager", + "page": "https://awesome-dsh-plugin.com/p/WSL043/dsh-chat-manager/", + "category": "session", + "description": { + "en": "Manages archived DSH chats with conversation search, restore, and confirmed safe permanent deletion.", + "zh": "管理 DSH 归档聊天,支持聊天记录搜索、恢复和二次确认的安全永久删除。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:WSL043/dsh-chat-manager", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/WSL043/dsh-chat-manager/main/docs/assets/archive-manager.en.png", + "https://raw.githubusercontent.com/WSL043/dsh-chat-manager/main/docs/assets/confirm-delete.en.png" + ] + }, + { + "name": "dsh-plugins#dsh-session-manager", + "owner": "wsxwj123", + "url": "https://github.com/wsxwj123/dsh-plugins/tree/main/packages/dsh-session-manager", + "page": "https://awesome-dsh-plugin.com/p/wsxwj123/dsh-plugins--packages-dsh-session-manager/", + "category": "session", + "description": { + "en": "Delete sessions with a 5-second undo and a recycle bin, plus an archive view to browse and unarchive, for the DSH Web GUI.", + "zh": "会话删除(5 秒可撤销 + 回收站)与归档视图(查看、取消归档)。" + }, + "npm": "@wsxwj123/dsh-session-manager", + "stars": 8, + "downloads": 97, + "install": "dsh plugin --profile web add @wsxwj123/dsh-session-manager", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/wsxwj123/dsh-plugins/main/assets/screenshots/session-manager.png" + ] + }, + { + "name": "dsh-session-manager", + "owner": "wuxiangru915", + "url": "https://github.com/wuxiangru915/dsh-session-manager", + "page": "https://awesome-dsh-plugin.com/p/wuxiangru915/dsh-session-manager/", + "category": "session", + "description": { + "en": "Session manager for the DSH Web UI: browse all and archived sessions, restore/unarchive, delete with two-step confirmation, and preview conversation content from the Settings panel.", + "zh": "DSH Web 会话管理插件:在设置页查看全部与已归档会话,支持恢复(取消归档)、二次确认删除与对话内容预览。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:wuxiangru915/dsh-session-manager", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/wuxiangru915/dsh-session-manager/main/assets/session-manager.png" + ] + }, + { + "name": "dsh-whale-companion", + "owner": "xianshu-virtuous", + "url": "https://github.com/xianshu-virtuous/dsh-whale-companion", + "page": "https://awesome-dsh-plugin.com/p/xianshu-virtuous/dsh-whale-companion/", + "category": "session", + "description": { + "en": "Automatically starts a fresh Web session near the context limit with the last complete turn, and adds an editable additive whale-maid persona with on-demand reload.", + "zh": "接近上下文上限时自动创建新 Web 会话并只转移最后一个完整回合,同时提供可编辑的追加式鲸鱼娘人格与按需重读工具。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:xianshu-virtuous/dsh-whale-companion", + "added": "2026-08-16" + }, + { + "name": "dsh-session-surgeon", + "owner": "xiaoshenming", + "url": "https://github.com/xiaoshenming/dsh-session-surgeon", + "page": "https://awesome-dsh-plugin.com/p/xiaoshenming/dsh-session-surgeon/", + "category": "session", + "description": { + "en": "Repair DeepSeek Harness sessions that refuse to load, with a sidebar Session surgeon overlay and dry-run repair.", + "zh": "修好打不开的 DeepSeek Harness 会话,侧栏「会话医生」可查看对话并默认 dry-run 修复。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:xiaoshenming/dsh-session-surgeon", + "added": "2026-08-19" + }, + { + "name": "dsh-vision-hub#bridge-preview", + "owner": "xing666173", + "url": "https://github.com/xing666173/dsh-vision-hub/tree/main/bridge-preview", + "page": "https://awesome-dsh-plugin.com/p/xing666173/dsh-vision-hub--bridge-preview/", + "category": "session", + "description": { + "en": "Inline preview for \\[图片: path] image bridge markers: after the marker renders, the image shows in chat automatically, keeping long instructions out of the conversation.", + "zh": "图片桥接内联预览:对话中 \\[图片: 路径] 标记渲染后自动显示图片,长指令不留在对话里。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:xing666173/dsh-vision-hub#path:/bridge-preview", + "added": "2026-08-23" + }, + { + "name": "dsh-session-actions", + "owner": "xingyingyuzhui", + "url": "https://github.com/xingyingyuzhui/dsh-session-actions", + "page": "https://awesome-dsh-plugin.com/p/xingyingyuzhui/dsh-session-actions/", + "category": "session", + "description": { + "en": "Session row menu extras for DeepSeek Harness: pin sessions, copy session id, export structured Markdown, and permanently delete sessions.", + "zh": "会话行菜单增强:置顶会话、复制会话 ID、导出结构化 Markdown、弹窗确认后永久删除会话。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:xingyingyuzhui/dsh-session-actions", + "added": "2026-08-17" + }, + { + "name": "dsh-session-delete", + "owner": "xohmai", + "url": "https://github.com/xohmai/dsh-session-delete", + "page": "https://awesome-dsh-plugin.com/p/xohmai/dsh-session-delete/", + "category": "session", + "description": { + "en": "Real session deletion with trash and restore, archived cleanup, and batch delete from settings.", + "zh": "真正删除会话:回收站可还原、归档清理、设置页批量删除。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:xohmai/dsh-session-delete", + "added": "2026-08-20" + }, + { + "name": "dsh-session-export", + "owner": "yangyongzhen", + "url": "https://github.com/yangyongzhen/dsh-session-export", + "page": "https://awesome-dsh-plugin.com/p/yangyongzhen/dsh-session-export/", + "category": "session", + "description": { + "en": "Export sessions to Markdown/JSON for review, replay, and per-session cost summaries.", + "zh": "会话导出为 Markdown/JSON,便于复盘、重放与单会话成本汇总。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:yangyongzhen/dsh-session-export", + "added": "2026-08-15" + }, + { + "name": "dsh-session-flow", + "owner": "YeqingTang", + "url": "https://github.com/YeqingTang/dsh-session-flow", + "page": "https://awesome-dsh-plugin.com/p/YeqingTang/dsh-session-flow/", + "category": "session", + "description": { + "en": "Cross-session archive: overview workbench, folded timeline, subagent lineage, full-text search, rule+LLM summaries, ZIP export, live tracking. Native session-page embeds: turn rail, docked right panel (live mode, pinned to original session on switch), rename (official-synced), conclusion digest, health monitoring + header chip (stall detection). Settings dialog section with 7 tunable behavior params.", + "zh": "跨会话档案柜:总览工作台、折叠时间线、子代理血缘、全文检索、双模式摘要、ZIP 导出、实时跟踪。会话流标签页嵌入原生会话页:轮次悬浮条、详情并入右栏(实时模式,切换会话可钉住原会话直播)、会话重命名(对齐官方即时同步)、结论摘要行、健康监控与头部芯片(疑似卡死识别)。设置弹窗独立分节,7 项行为参数可调。" + }, + "npm": "dsh-session-flow", + "stars": 4, + "downloads": 690, + "install": "dsh plugin --profile web add dsh-session-flow", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/YeqingTang/dsh-session-flow/main/assets/screenshots/overview.png", + "https://raw.githubusercontent.com/YeqingTang/dsh-session-flow/main/assets/screenshots/session-flow-tab.png", + "https://raw.githubusercontent.com/YeqingTang/dsh-session-flow/main/assets/screenshots/turn-rail.png", + "https://raw.githubusercontent.com/YeqingTang/dsh-session-flow/main/assets/screenshots/fulltext-search.png", + "https://raw.githubusercontent.com/YeqingTang/dsh-session-flow/main/assets/screenshots/dock-right.png", + "https://raw.githubusercontent.com/YeqingTang/dsh-session-flow/main/assets/screenshots/folded-detail.png" + ] + }, + { + "name": "dsh-session-hotkeys", + "owner": "YEYEYEYESHIFU", + "url": "https://github.com/YEYEYEYESHIFU/dsh-session-hotkeys", + "page": "https://awesome-dsh-plugin.com/p/YEYEYEYESHIFU/dsh-session-hotkeys/", + "category": "session", + "description": { + "en": "Session hotkeys for the DSH Web GUI: switch sessions like browser tabs (positional Alt+1-9, pinned slots, previous/next, new/archive/rename), keyboard model switching via Alt+M, alternate send and archive confirmation, all rebindable with Windows/macOS presets.", + "zh": "Web 会话热键插件:像切浏览器标签页一样用键盘管理会话——Alt+1-9 顺序切换、固定槽位、上/下一个、新建/归档/重命名、Alt+M 键盘切换模型与备用发送,键位可在面板录制重绑,Windows/macOS 双预设。" + }, + "npm": "dsh-session-hotkeys", + "stars": 3, + "downloads": 935, + "install": "dsh plugin --profile web add dsh-session-hotkeys", + "added": "2026-08-16" + }, + { + "name": "dsh-argp", + "owner": "yoza10635", + "url": "https://github.com/yoza10635/dsh-argp", + "page": "https://awesome-dsh-plugin.com/p/yoza10635/dsh-argp/", + "category": "session", + "description": { + "en": "Deterministic context compaction: prunes conversation atoms in reference-graph topological order with no LLM calls during compaction, and keeps pruned atoms recallable by marker.", + "zh": "确定性上下文压缩:按引用图拓扑序摘除对话原子,压缩阶段不调用 LLM;被摘除的原子保留 marker,可按需回拉。" + }, + "npm": "dsh-argp", + "stars": 4, + "downloads": 1381, + "install": "dsh plugin --profile web add dsh-argp", + "added": "2026-08-18" + }, + { + "name": "dsh-explain", + "owner": "yuezengwu", + "url": "https://github.com/yuezengwu/dsh-explain", + "page": "https://awesome-dsh-plugin.com/p/yuezengwu/dsh-explain/", + "category": "session", + "description": { + "en": "Local-first learning mode: cross-session learning threads with per-source explanations.", + "zh": "本地优先学习模式:跨会话全局学习线程、按来源讲解。" + }, + "npm": null, + "stars": 12, + "downloads": null, + "install": "dsh plugin --profile web add github:yuezengwu/dsh-explain", + "added": "2026-08-13" + }, + { + "name": "dsh-period-report", + "owner": "zhengjy01", + "url": "https://github.com/zhengjy01/dsh-period-report", + "page": "https://awesome-dsh-plugin.com/p/zhengjy01/dsh-period-report/", + "category": "session", + "description": { + "en": "Free-interval session reports with AI-narrated daily/weekly digests over any date range, plus every-N-days scheduled reminders with system notifications (macOS / Linux).", + "zh": "自由周期会话报告:任意日期区间的 AI 叙事日报/周报/月报,支持每隔 N 天定时提醒并弹系统通知(macOS / Linux)。" + }, + "npm": "dsh-period-report", + "stars": 1, + "downloads": 405, + "install": "dsh plugin --profile web add dsh-period-report", + "added": "2026-08-15" + }, + { + "name": "dsh-share", + "owner": "zljr", + "url": "https://github.com/zljr/dsh-share", + "page": "https://awesome-dsh-plugin.com/p/zljr/dsh-share/", + "category": "session", + "description": { + "en": "Share the current session over the LAN as a read-only, token-guarded HTML snapshot with session stats and Markdown rendering.", + "zh": "将当前会话以只读、token 保护的 HTML 快照分享到局域网,附带会话统计与 Markdown 渲染。" + }, + "npm": "@zljr/dsh-share", + "stars": 3, + "downloads": 586, + "install": "dsh plugin --profile web add @zljr/dsh-share", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/zljr/dsh-share/master/assets/share-dialog.png", + "https://raw.githubusercontent.com/zljr/dsh-share/master/assets/share-page-light.png", + "https://raw.githubusercontent.com/zljr/dsh-share/master/assets/share-page-dark.png" + ] + }, + { + "name": "dsh-session-explorer", + "owner": "Zn-Dk", + "url": "https://github.com/Zn-Dk/dsh-session-explorer", + "page": "https://awesome-dsh-plugin.com/p/Zn-Dk/dsh-session-explorer/", + "category": "session", + "description": { + "en": "Message-level full-text search across DSH sessions via an FTS5 trigram index (user / assistant / steering / tool with per-kind filters), deduplicated results across fork/continued sessions, read-only context preview with auto-scroll to the focused message, one-click jump to the real session, and incremental/full index rebuild with health check; zh/en i18n follows the Host locale service.", + "zh": "DSH 会话消息级全文检索浏览器:FTS5 trigram 索引按消息检索(用户/助手/系统注入/工具四类,可按类型筛选),fork/续接会话结果自动去重,只读上下文预览自动滚动定位焦点消息,一键跳转真实会话;支持增量/全量重建索引与健康检查;界面中英双语跟随 Host locale 服务。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Zn-Dk/dsh-session-explorer", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/Zn-Dk/dsh-session-explorer/HEAD/assets/scr-message-search.png", + "https://raw.githubusercontent.com/Zn-Dk/dsh-session-explorer/HEAD/assets/scr-timeline.png" + ] + }, + { + "name": "dsh-session-repair", + "owner": "Zn-Dk", + "url": "https://github.com/Zn-Dk/dsh-session-repair", + "page": "https://awesome-dsh-plugin.com/p/Zn-Dk/dsh-session-repair/", + "category": "session", + "description": { + "en": "Diagnose and safely repair corrupted DSH session history: raw zstd/JSONL validation, tool-call ID repair, single-slot pre-repair backup, restore, and an audit trail.", + "zh": "诊断并安全修复损坏的 DSH 会话历史:raw zstd/JSONL 校验、tool-call ID 修复、单槽 pre-repair 备份与恢复、审计记录。" + }, + "npm": "dsh-session-repair", + "stars": 1, + "downloads": 1096, + "install": "dsh plugin --profile web add dsh-session-repair", + "added": "2026-08-24", + "screenshots": [ + "https://raw.githubusercontent.com/Zn-Dk/dsh-session-repair/HEAD/assets/session-health-check-repairable.png", + "https://raw.githubusercontent.com/Zn-Dk/dsh-session-repair/HEAD/assets/session-health-check-repaired.png", + "https://raw.githubusercontent.com/Zn-Dk/dsh-session-repair/HEAD/assets/session-health-check.png" + ] + }, + { + "name": "dsh-shelf", + "owner": "zoahdev", + "url": "https://github.com/zoahdev/dsh-shelf", + "page": "https://awesome-dsh-plugin.com/p/zoahdev/dsh-shelf/", + "category": "session", + "description": { + "en": "Session lifecycle CLI: export (md/json/jsonl), archive/restore, trash, search, and stats. Read-only by default; nothing is ever deleted by the engine.", + "zh": "会话生命周期 CLI:导出(md/json/jsonl)、归档/恢复、回收站、搜索与统计。默认只读;引擎从不删除任何东西。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:zoahdev/dsh-shelf", + "added": "2026-08-15" + }, + { + "name": "dsh-minimal-first-turn", + "owner": "ZRui-C", + "url": "https://github.com/ZRui-C/dsh-minimal-first-turn", + "page": "https://awesome-dsh-plugin.com/p/ZRui-C/dsh-minimal-first-turn/", + "category": "session", + "description": { + "en": "Minimal-compatible first turn for Web root sessions: starts with persistent bash and str_replace_editor, removes automatic workspace and skill context, then restores the selected preset after the first tool call or reply; includes a persistent composer toggle.", + "zh": "Web 根会话的首轮精简:先使用持久 bash 与 str_replace_editor,移除自动工作区和技能上下文;首次工具调用或回复后恢复所选预设,带持久 composer 开关。" + }, + "npm": "dsh-minimal-first-turn", + "stars": 2, + "downloads": 334, + "install": "dsh plugin --profile web add dsh-minimal-first-turn", + "added": "2026-08-16" + }, + { + "name": "dsh-project-memory", + "owner": "00080000", + "url": "https://github.com/00080000/dsh-project-memory", + "page": "https://awesome-dsh-plugin.com/p/00080000/dsh-project-memory/", + "category": "memory", + "description": { + "en": "Project memory for DeepSeek Harness: project files are indexed into searchable summaries as they are read, so after context loss the agent retrieves hits on demand instead of re-reading files, with source citations and BM25 ranking.", + "zh": "为 DeepSeek Harness 提供的项目记忆插件:项目文件在读取时索引为可检索摘要,上下文失效后按需检索命中,无需重读文件,自带出处与 BM25 排序。" + }, + "npm": "@yolk_vat-y/dsh-project-memory", + "stars": 2, + "downloads": 340, + "install": "dsh plugin --profile web add @yolk_vat-y/dsh-project-memory", + "added": "2026-08-20" + }, + { + "name": "runtime36", + "owner": "398894496-arch", + "url": "https://github.com/398894496-arch/runtime36", + "page": "https://awesome-dsh-plugin.com/p/398894496-arch/runtime36/", + "category": "memory", + "description": { + "en": "Local Obsidian vault as the agent knowledge store, with read-only DSH tools that route status, preference, correction, memory, project, search, and suggest to the matching page.", + "zh": "把本地 Obsidian 库当作 Agent 知识库,并用只读 DSH 工具把状态、偏好、纠错、记忆、项目、搜索与建议路由到对应页面。" + }, + "npm": null, + "stars": 13, + "downloads": null, + "install": "dsh plugin --profile web add github:398894496-arch/runtime36", + "added": "2026-08-23" + }, + { + "name": "dsh-memory-setup", + "owner": "863683348", + "url": "https://github.com/863683348/dsh-memory-setup", + "page": "https://awesome-dsh-plugin.com/p/863683348/dsh-memory-setup/", + "category": "memory", + "description": { + "en": "Local, auditable personal memory for DeepSeek Harness: preferences, project conventions, workflows and error lessons persisted as a changelogged JSON file in the workspace, with onboarding, auto project-convention extraction and evidence-backed lessons.", + "zh": "解决 AI 金鱼脑:本地可审计的个人记忆层——偏好、项目约定、工作流与纠错教训,以带变更日志与完整性校验的 JSON 持久化在工作区,支持一次性设置、项目约定自动提取、证据化教训与快照恢复。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:863683348/dsh-memory-setup", + "added": "2026-08-19" + }, + { + "name": "dsh-plugin-focus", + "owner": "863683348", + "url": "https://github.com/863683348/dsh-plugin-focus", + "page": "https://awesome-dsh-plugin.com/p/863683348/dsh-plugin-focus/", + "category": "memory", + "description": { + "en": "Focus board for DeepSeek Harness agents: durable, model-maintained notes in the session workspace that pin the objective, constraints, and decisions across compaction and sessions, with automatic context injection, archive on clear, and an optional web panel.", + "zh": "为 DeepSeek Harness agent 提供持久化专注板:在会话工作区维护目标、约束与决策笔记,跨压缩与会话存活,支持自动注入上下文、清空归档与可选 Web 面板。" + }, + "npm": "dsh-plugin-focus", + "stars": 2, + "downloads": 323, + "install": "dsh plugin --profile web add dsh-plugin-focus", + "added": "2026-08-16" + }, + { + "name": "harness-desktop#plugins/harness-memory", + "owner": "988hj7tczd-oss", + "url": "https://github.com/988hj7tczd-oss/harness-desktop/tree/main/plugins/harness-memory", + "page": "https://awesome-dsh-plugin.com/p/988hj7tczd-oss/harness-desktop--plugins-harness-memory/", + "category": "memory", + "description": { + "en": "Cross-session memory for DeepSeek Harness via the official storage domain: memories persist through ctx.storage (storageDomain table) and are injected into the system prompt through a registered section (memory:recall), with memory_save / memory_forget tools so the agent reads and writes its own memory (maxMemories configurable).", + "zh": "跨会话记忆插件:用官方 ctx.storage 存储域持久化记忆,注册 system-prompt section(memory:recall)把记忆注入模型上下文,提供 memory_save / memory_forget 工具让 agent 自行存取记忆(maxMemories 可配置)。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:988hj7tczd-oss/harness-desktop#path:/plugins/harness-memory", + "added": "2026-08-23" + }, + { + "name": "graph-memory", + "owner": "adoresever", + "url": "https://github.com/adoresever/graph-memory", + "page": "https://awesome-dsh-plugin.com/p/adoresever/graph-memory/", + "category": "memory", + "description": { + "en": "Traceable, searchable cross-session memory for DeepSeek Harness — conversation knowledge as typed graph nodes (TASK/SKILL/EVENT) and typed edges.", + "zh": "DeepSeek Harness 的可追溯、可检索跨会话记忆:把对话知识沉淀为带类型的图节点(任务/技能/事件)与关系边。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:adoresever/graph-memory", + "added": "2026-08-26" + }, + { + "name": "dsh-active-context-pruning", + "owner": "aerince", + "url": "https://github.com/aerince/dsh-active-context-pruning", + "page": "https://awesome-dsh-plugin.com/p/aerince/dsh-active-context-pruning/", + "category": "memory", + "description": { + "en": "Model-authored context pruning for DeepSeek Harness through the official compaction API.", + "zh": "面向 DeepSeek Harness 的模型驱动上下文裁剪插件,通过官方 compaction API 释放上下文空间。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:aerince/dsh-active-context-pruning", + "added": "2026-08-15" + }, + { + "name": "ReMe#typescript", + "owner": "agentscope-ai", + "url": "https://github.com/agentscope-ai/ReMe/tree/main/typescript", + "page": "https://awesome-dsh-plugin.com/p/agentscope-ai/ReMe--typescript/", + "category": "memory", + "description": { + "en": "Connects DeepSeek Harness to ReMe's local-first, self-evolving personal knowledge base: automatically captures completed main-agent conversations as user-owned Markdown memory, searches conversations and source material through reme_search with BM25, optional embeddings, and wikilink expansion, and schedules daily memory consolidation.", + "zh": "将 DeepSeek Harness 接入 ReMe 本地优先、自进化的个人知识库:自动把已完成的主 Agent 对话沉淀为用户掌控的 Markdown 记忆,通过 reme_search 结合 BM25、可选向量检索和 wikilink 展开搜索对话与资料,并按日整理长期记忆。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:agentscope-ai/ReMe#path:/typescript", + "added": "2026-08-27" + }, + { + "name": "dsh-auto-memory", + "owner": "Aik358", + "url": "https://github.com/Aik358/dsh-auto-memory", + "page": "https://awesome-dsh-plugin.com/p/Aik358/dsh-auto-memory/", + "category": "memory", + "description": { + "en": "Cache-friendly three-layer memory for DSH: lean auto injection, per-turn AI consolidation, proactive calendar reminders and warm greetings, plus memory inheritance from other AI tools.", + "zh": "DSH 三层自动记忆引擎:缓存友好的精简注入与每轮 AI 自动沉淀,主动日历提醒与暖心问候,可继承其他 AI 工具的记忆。" + }, + "npm": "@a9i5k4/dsh-auto-memory", + "stars": 37, + "downloads": 5640, + "install": "dsh plugin --profile web add @a9i5k4/dsh-auto-memory", + "added": "2026-08-15" + }, + { + "name": "dsh-negative-ledger", + "owner": "akslcw", + "url": "https://github.com/akslcw/dsh-negative-ledger", + "page": "https://awesome-dsh-plugin.com/p/akslcw/dsh-negative-ledger/", + "category": "memory", + "description": { + "en": "Evidence-bound negative-knowledge ledger: persists disproven paths (command_failed, file_missing) with their outcome and precondition evidence, then warns or blocks repeat attempts until the evidence changes.", + "zh": "证据约束的负面知识账本:记录被证伪的路径(command_failed / file_missing)及其结果与前置条件证据,重复尝试时警告或拦截,证据变化后自动解除。" + }, + "npm": "@akslcw/dsh-negative-ledger", + "stars": 3, + "downloads": 800, + "install": "dsh plugin --profile web add @akslcw/dsh-negative-ledger", + "added": "2026-08-15" + }, + { + "name": "dsh-butler-memory", + "owner": "AndyYang12345", + "url": "https://github.com/AndyYang12345/dsh-butler-memory", + "page": "https://awesome-dsh-plugin.com/p/AndyYang12345/dsh-butler-memory/", + "category": "memory", + "description": { + "en": "PostgreSQL-backed durable memory for DSH agents: MCP memory tools plus a Web panel for reviewing memories and candidates.", + "zh": "PostgreSQL 持久个人记忆:MCP 记忆工具 + 可查看记忆与候选的 Web 面板。" + }, + "npm": "dsh-butler-memory", + "stars": 1, + "downloads": 1056, + "install": "dsh plugin --profile web add dsh-butler-memory", + "added": "2026-08-18" + }, + { + "name": "dsh-memory", + "owner": "aqsk-BLG", + "url": "https://github.com/aqsk-BLG/dsh-memory", + "page": "https://awesome-dsh-plugin.com/p/aqsk-BLG/dsh-memory/", + "category": "memory", + "description": { + "en": "Layered file memory for DeepSeek Harness with workspace-scoped USER/MEMORY notes, background consolidation, and hybrid session recall.", + "zh": "DeepSeek Harness 分层文件记忆,提供工作区隔离的 USER/MEMORY 笔记、后台沉淀与混合会话召回。" + }, + "npm": "dsh-file-memory", + "tarball": "https://github.com/aqsk-BLG/dsh-memory/releases/download/v1.3.2/dsh-file-memory-1.3.2.tgz", + "stars": 3, + "downloads": 595, + "install": "dsh plugin --profile web add dsh-file-memory", + "added": "2026-08-19" + }, + { + "name": "dsh-memory-connect", + "owner": "Asher-2000", + "url": "https://github.com/Asher-2000/dsh-memory-connect", + "page": "https://awesome-dsh-plugin.com/p/Asher-2000/dsh-memory-connect/", + "category": "memory", + "description": { + "en": "Cross-session memory plugin for DSH (v0.6.1, npm @asherliner/dsh-memory-connect): auto-extraction, SQLite FTS5 indexing, RRF semantic recall injected into the system prompt every turn, scheduled maintenance, LLM-powered consolidation via ctx.llm, context explosion prevention, and global Soul identity via ~/.dsh/soul.md. v0.5.0 added temporal context graph (valid_from/valid_until/supersedes, append-only corrections via reviseMemory), trust model (recalled history injected as untrusted reference, current instruction always wins), turn-end summarization. v0.6.0 added local semantic embeddings (BAAI/bge-small-zh-v1.5 via embed_server.py, cosine + RRF fusion with FTS5, no cloud calls, ~232 msgs/sec on CPU). v0.6.1 fixed ~ expansion in path config. Zero-config install.", + "zh": "跨会话记忆插件(v0.6.1,npm 包 @asherliner/dsh-memory-connect):自动提取、SQLite FTS5 索引、RRF 语义召回(每轮注入系统提示词)、定时维护、LLM 智能合并(自动接入 ctx.llm)、上下文爆炸防护、全局身份 Soul(~/.dsh/soul.md)。v0.5.0 新增:时态上下文图谱(valid_from/valid_until/supersedes,reviseMemory 追加式修正)、信任模型(历史记忆作为不可信参考注入,当前指令绝对优先)、轮末自动摘要。v0.6.0 新增:本地语义 embedding(BAAI/bge-small-zh-v1.5,embed_server.py 提供,余弦相似度 + 与 FTS5 的 RRF 融合,纯本地无云端调用,CPU 约 232 条/秒)。v0.6.1 修复 path 配置 ~ 展开。零配置装完即用。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:Asher-2000/dsh-memory-connect", + "added": "2026-08-20" + }, + { + "name": "dsh-memory-protocol", + "owner": "baaai123", + "url": "https://github.com/baaai123/dsh-memory-protocol", + "page": "https://awesome-dsh-plugin.com/p/baaai123/dsh-memory-protocol/", + "category": "memory", + "description": { + "en": "Memory protocol enforcement plugin: bridges the opencode-memory MCP server, forces memory_weave before tool calls, auto-ingests turns, and injects memory context into agent steps.", + "zh": "记忆强制协议插件:桥接 opencode-memory MCP 服务器,工具调用前强制 memory_weave、每轮自动存档、自动注入记忆上下文。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:baaai123/dsh-memory-protocol", + "added": "2026-08-15", + "screenshots": [ + "https://raw.githubusercontent.com/baaai123/dsh-memory-protocol/HEAD/assets/screenshots/cover.png", + "https://raw.githubusercontent.com/baaai123/dsh-memory-protocol/HEAD/assets/screenshots/architecture%402x.png", + "https://raw.githubusercontent.com/baaai123/dsh-memory-protocol/HEAD/assets/screenshots/enforcement-demo.png", + "https://raw.githubusercontent.com/baaai123/dsh-memory-protocol/HEAD/assets/screenshots/config.png" + ] + }, + { + "name": "dsh-eli-mode#packages/eli-mode", + "owner": "CeilCelia", + "url": "https://github.com/CeilCelia/dsh-eli-mode/tree/main/packages/eli-mode", + "page": "https://awesome-dsh-plugin.com/p/CeilCelia/dsh-eli-mode--packages-eli-mode/", + "category": "memory", + "description": { + "en": "Knowledge-base-driven agent preset: wiki long-term memory, a web KB UI, a management page and optional UI polish.", + "zh": "知识库驱动的 Agent 预设:wiki 长期记忆、网页知识库、管理页、可选界面润色。" + }, + "npm": "dsh-eli-mode", + "stars": 6, + "downloads": 2140, + "install": "dsh plugin --profile web add dsh-eli-mode", + "added": "2026-08-15" + }, + { + "name": "dsh-evolve", + "owner": "chenzheshushi-commits", + "url": "https://github.com/chenzheshushi-commits/dsh-evolve", + "page": "https://awesome-dsh-plugin.com/p/chenzheshushi-commits/dsh-evolve/", + "category": "memory", + "description": { + "en": "Cross-session memory with zero-token deterministic recall (bigram-Jaccard fused with FTS5 BM25 via RRF), a tiered approval gate that auto-confirms only reversible user-anchored writes, and reinforcement that strengthens repeated observations. Crystallizes procedures into SKILL.md files that refine in place, curated through an active-stale-archived lifecycle with reversible archive and rollback. v0.4.2 adds a controlled-prune settings page: reversible soft-delete (tombstone, restorable), pinned three-tier protection, read-only heat ordering, and two-stage preview-then-execute with idempotent apply. Anti-bloat convergence for skills and memory, background per-turn review in an isolated call. Requires Node 22.5+.", + "zh": "跨会话记忆,零 token 确定性召回(bigram-Jaccard 与 FTS5 BM25 经 RRF 融合,中文分词精确);分级审批门只自动确认可逆且源自用户原话的写入,其余待人工确认;重复观察会强化已有理解而非堆叠重复。过程性知识结晶为 SKILL.md,就地精炼升级,走 active-stale-archived 生命周期治理,归档可逆、可回滚。v0.4.2 新增受控剪枝设置页:可逆软删除(墓碑标记、可恢复)、pinned 三层保护、只读 heat 冷度排序、两阶段预览-执行且幂等应用。含 skill 与记忆双层防臃肿收敛、每轮结束的隔离式后台复盘。需要 Node 22.5+。" + }, + "npm": null, + "tarball": "https://github.com/chenzheshushi-commits/dsh-evolve/releases/latest/download/dsh-evolve.tgz", + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/chenzheshushi-commits/dsh-evolve/releases/latest/download/dsh-evolve.tgz\"", + "added": "2026-08-24" + }, + { + "name": "Co-Engram#dsh-plugin", + "owner": "Co-Engram", + "url": "https://github.com/Co-Engram/Co-Engram/tree/main/packages/dsh-plugin", + "page": "https://awesome-dsh-plugin.com/p/Co-Engram/Co-Engram--packages-dsh-plugin/", + "category": "memory", + "description": { + "en": "Self-evolving team memory as plain Markdown in git: a native Cordis plugin that registers 38 bare-name memory tools plus a prompt-signals section re-evaluated at every assembly, with RPE reinforcement, decay and sleep consolidation; shares one data repo with its Claude Code (MCP) and OpenClaw hosts; verified against DSH 0.1.0-rc.6.", + "zh": "自进化团队记忆,以纯 Markdown 存于 Git:原生 Cordis 插件注册 38 个裸名记忆工具,并按每次组装动态注入 prompt-signals 段;含 RPE 强化、衰减与睡眠巩固;与 Claude Code(MCP)、OpenClaw 宿主共享同一数据仓;已对 DSH 0.1.0-rc.6 实测。" + }, + "npm": "@co-engram/dsh", + "stars": 11, + "downloads": 784, + "install": "dsh plugin --profile web add @co-engram/dsh", + "added": "2026-08-16" + }, + { + "name": "dsh-plugins#dsh-memories", + "owner": "DamonKoy", + "url": "https://github.com/DamonKoy/dsh-plugins/tree/main/packages/dsh-memories", + "page": "https://awesome-dsh-plugin.com/p/DamonKoy/dsh-plugins--packages-dsh-memories/", + "category": "memory", + "description": { + "en": "Project-scoped persistent key-value memories with set/get/list/delete/search model tools and JSON-file storage.", + "zh": "项目作用域持久化键值记忆:set/get/list/delete/search 模型工具,JSON 文件存储。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:DamonKoy/dsh-plugins#path:/packages/dsh-memories", + "added": "2026-08-17" + }, + { + "name": "dsh-client-ui-obsidian-memory", + "owner": "detongz", + "url": "https://github.com/detongz/dsh-client-ui-obsidian-memory", + "page": "https://awesome-dsh-plugin.com/p/detongz/dsh-client-ui-obsidian-memory/", + "category": "memory", + "description": { + "en": "Persistent AI memory backed by a local Obsidian/Codex vault: five obsidian_memory_* read/write/search tools plus a sidebar vault browser.", + "zh": "基于本地 Obsidian/Codex 知识库的持久 AI 记忆:5 个 obsidian_memory_* 读写搜索工具,外加侧边栏知识库浏览器。" + }, + "npm": "dsh-client-ui-obsidian-memory", + "stars": 13, + "downloads": 1802, + "install": "dsh plugin --profile web add dsh-client-ui-obsidian-memory", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/detongz/dsh-client-ui-obsidian-memory/main/assets/screenshot-panel.png" + ] + }, + { + "name": "StrataGate-AgentMemory", + "owner": "diqierjia", + "url": "https://github.com/diqierjia/StrataGate-AgentMemory/tree/main/integrations/deepseek-harness", + "page": "https://awesome-dsh-plugin.com/p/diqierjia/StrataGate-AgentMemory--integrations-deepseek-harness/", + "category": "memory", + "description": { + "en": "StrataGate gives DeepSeek Harness evolving, local-first long-term memory for user preferences, project decisions, conversations, and tool results across sessions; it layers history with user-tunable time decay, reinforces memories through real answer use, shows workspace-aware usage counts, and keeps recalled evidence traceable to its original messages.", + "zh": "StrataGate 为 DeepSeek Harness 自动构建持续演化、本地优先的长期记忆:跨会话承接用户偏好、项目决策、历史对话和工具结果,通过用户可调的时间衰减分层沉淀历史,按真实回答使用强化记忆,显示按工作区识别的使用计数,并让召回证据可追溯到原始消息。" + }, + "npm": "stratagate-dsh", + "tarball": "https://github.com/diqierjia/StrataGate-AgentMemory/releases/download/stratagate-dsh-v0.2.35/stratagate-dsh-0.2.35.tgz", + "stars": 8, + "downloads": 1299, + "install": "dsh plugin --profile web add stratagate-dsh", + "added": "2026-08-16", + "screenshots": [ + "https://raw.githubusercontent.com/diqierjia/StrataGate-AgentMemory/HEAD/integrations/deepseek-harness/docs/assets/stratagate-knowledge-graph.png", + "https://raw.githubusercontent.com/diqierjia/StrataGate-AgentMemory/HEAD/integrations/deepseek-harness/docs/assets/stratagate-short-term-memory.png" + ] + }, + { + "name": "dsh-handoff", + "owner": "FleetingEcho", + "url": "https://github.com/FleetingEcho/dsh-handoff", + "page": "https://awesome-dsh-plugin.com/p/FleetingEcho/dsh-handoff/", + "category": "memory", + "description": { + "en": "Self-maintaining handoff memory per working directory and git branch: records turns, folds them into concise Markdown, and injects the result into future sessions from ~/.agent/agent-handoff, byte-compatible with pi-handoff.", + "zh": "每个工作目录与 git 分支自动维护 handoff.md:记录回合、折叠为简洁 Markdown 并注入后续会话,存储于 ~/.agent/agent-handoff,与 pi-handoff 字节级兼容。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:FleetingEcho/dsh-handoff", + "added": "2026-08-15" + }, + { + "name": "dsh-memory", + "owner": "flymysql", + "url": "https://github.com/flymysql/dsh-memory", + "page": "https://awesome-dsh-plugin.com/p/flymysql/dsh-memory/", + "category": "memory", + "description": { + "en": "Cross-session memory vault: remember / recall / forget tools, per-turn prompt injection, and a settings-page entry browser.", + "zh": "跨会话记忆库:remember / recall / forget 工具、每轮提示注入与设置页条目浏览。" + }, + "npm": "dsh-memory-vault", + "stars": 4, + "downloads": 869, + "install": "dsh plugin --profile web add dsh-memory-vault", + "added": "2026-08-14" + }, + { + "name": "sgme", + "owner": "freehul", + "url": "https://github.com/freehul/sgme", + "page": "https://awesome-dsh-plugin.com/p/freehul/sgme/", + "category": "memory", + "description": { + "en": "ShiGuang Memory Engine (SGME) bridge: multi-agent shared long-term memory via HTTP — L0/L1/L1.5/L2 distillation, scenario-based injection, unified search, and proactive care signals (memory_search / wiki_search / signal_pull / signal_claim / signal_ack), installable as `dsh-sgme`.", + "zh": "拾光记忆引擎(SGME)桥接:多智能体共享长期记忆(HTTP)—— L0/L1/L1.5/L2 分层提炼、按场景注入、统一检索、主动关怀信号(memory_search / wiki_search / signal_pull / signal_claim / signal_ack),npm 包名 `dsh-sgme`。" + }, + "npm": null, + "stars": 12, + "downloads": null, + "install": "dsh plugin --profile web add github:freehul/sgme", + "added": "2026-08-14" + }, + { + "name": "dsh-hybrid-memory", + "owner": "Frog755", + "url": "https://github.com/Frog755/dsh-hybrid-memory", + "page": "https://awesome-dsh-plugin.com/p/Frog755/dsh-hybrid-memory/", + "category": "memory", + "description": { + "en": "Three-layer local memory for DeepSeek Harness: L1 frozen-snapshot memory (MEMORY.md/USER.md injected into the system prompt, prefix-cache friendly) + L2 searchable knowledge base (facts + SQLite FTS5, Chinese search via sliding-window tokenization) + L3 import from Hermes/Claude Code/Codex/WorkBuddy with dedup. Auto-injects relevant facts on the first step or memory trigger words (agent/pre-step) and after skill loads (tools/post-execute); data stays on the local drive.", + "zh": "DSH 本地混合记忆插件:L1 冻结快照(MEMORY.md/USER.md 注入 system prompt,保 prefix cache)+ L2 可检索知识库(facts + SQLite FTS5,滑窗分词支持中文检索)+ L3 从 Hermes/Claude Code/Codex/WorkBuddy 导入(账本去重);会话首步或记忆触发词自动注入相关事实,skill 加载后自动召回;数据全部留在本地。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:Frog755/dsh-hybrid-memory", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/Frog755/dsh-hybrid-memory/HEAD/assets/architecture.svg", + "https://raw.githubusercontent.com/Frog755/dsh-hybrid-memory/HEAD/assets/plugin-list.png" + ] + }, + { + "name": "sage-mem", + "owner": "gezi-wen", + "url": "https://github.com/gezi-wen/sage-mem", + "page": "https://awesome-dsh-plugin.com/p/gezi-wen/sage-mem/", + "category": "memory", + "description": { + "en": "File-based cross-session memory: plain Markdown memory files with automatic retrieval injection, format-compatible with Claude Code's CLAUDE.md and memory files, so migrating is a manual file copy.", + "zh": "基于文件的跨会话记忆:纯 Markdown 记忆文件,检索结果自动注入;格式与 Claude Code 的 CLAUDE.md 及记忆文件兼容,迁移即手工复制文件。" + }, + "npm": "sage-mem", + "stars": 5, + "downloads": 339, + "install": "dsh plugin --profile web add sage-mem", + "added": "2026-08-17" + }, + { + "name": "dsh-memory-gate", + "owner": "GIT121995", + "url": "https://github.com/GIT121995/dsh-memory-gate", + "page": "https://awesome-dsh-plugin.com/p/GIT121995/dsh-memory-gate/", + "category": "memory", + "description": { + "en": "Bounded local memory with CBDC authority gating: SQLite + FTS5 claims, scoped recall with explainable use/verify/ignore decisions and a full audit trail, /memory commands, ≤3-claim/1200-char injection per call, no extra model call.", + "zh": "有界本地记忆 + CBDC 权威门控:SQLite + FTS5 claims,作用域召回并给出可解释的采用/核验/忽略决策与完整审计轨迹,/memory 命令,每次注入 ≤3 条/1200 字符,不增加额外模型调用。" + }, + "npm": "dsh-memory-gate", + "stars": 3, + "downloads": 1949, + "install": "dsh plugin --profile web add dsh-memory-gate", + "added": "2026-08-14" + }, + { + "name": "dsh-headroom", + "owner": "giter00", + "url": "https://github.com/giter00/dsh-headroom", + "page": "https://awesome-dsh-plugin.com/p/giter00/dsh-headroom/", + "category": "memory", + "description": { + "en": "Automatic context compression for DeepSeek Harness: compresses tool outputs before they reach the model and keeps every lossy compression reversible via CCR retrieval tools.", + "zh": "面向 DeepSeek Harness 的自动上下文压缩插件:在工具输出进入模型前压缩,并通过 CCR 检索工具保持所有有损压缩可逆。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:giter00/dsh-headroom", + "added": "2026-08-18" + }, + { + "name": "dsh-palate", + "owner": "guo6x", + "url": "https://github.com/guo6x/dsh-palate", + "page": "https://awesome-dsh-plugin.com/p/guo6x/dsh-palate/", + "category": "memory", + "description": { + "en": "An accumulated design-taste corpus for DSH agents: feed good and bad examples, distill principles, and review new designs against learned judgment rather than a fixed ruleset.", + "zh": "给 DSH agent 的积累型设计品味语料库:喂好坏例子、提炼原则,用学到的判断评审新设计,而不是套固定规则。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:guo6x/dsh-palate", + "added": "2026-08-27" + }, + { + "name": "dsh-qrcode", + "owner": "hellosky983", + "url": "https://github.com/hellosky983/dsh-qrcode", + "page": "https://awesome-dsh-plugin.com/p/hellosky983/dsh-qrcode/", + "category": "memory", + "description": { + "en": "Offline QR code (SVG/PNG/ASCII) and barcode (Code128/EAN-13) generator for DeepSeek Harness, no network and no shell.", + "zh": "DeepSeek Harness 离线二维码(SVG/PNG/ASCII)与条码(Code128/EAN-13)生成器,无网络、无 shell。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:hellosky983/dsh-qrcode", + "added": "2026-08-15" + }, + { + "name": "dsh-skillradar", + "owner": "hellosky983", + "url": "https://github.com/hellosky983/dsh-skillradar", + "page": "https://awesome-dsh-plugin.com/p/hellosky983/dsh-skillradar/", + "category": "memory", + "description": { + "en": "Scans the skills visible to the current session and ranks them by relevance to the recent conversation.", + "zh": "扫描当前会话可见的技能,按与最近对话的相关性排序推荐。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:hellosky983/dsh-skillradar", + "added": "2026-08-15" + }, + { + "name": "dsh-native-memory", + "owner": "highland0971", + "url": "https://github.com/highland0971/dsh-native-memory", + "page": "https://awesome-dsh-plugin.com/p/highland0971/dsh-native-memory/", + "category": "memory", + "description": { + "en": "Native per-workspace memory on the harness's own seams: facts and a bounded always-on profile on the storage-domain JSON unit, approval-gated writes with `(sessionId, seq)` citations, deterministic recall plus session-query FTS over past sessions — no external server, no custom SQLite.", + "zh": "基于 DSH 原生接缝的按工作区记忆:事实与有界常驻档案落在 storage-domain 的 JSON 单元上,写入经人工审批并带 `(sessionId, seq)` 引证,确定性召回 + session-query 全文检索历史会话——无外部服务器、无自建 SQLite。" + }, + "npm": "dsh-native-memory", + "stars": 1, + "downloads": 974, + "install": "dsh plugin --profile web add dsh-native-memory", + "added": "2026-08-15" + }, + { + "name": "dsh-knowledge-base", + "owner": "htcqp802", + "url": "https://github.com/htcqp802/dsh-knowledge-base", + "page": "https://awesome-dsh-plugin.com/p/htcqp802/dsh-knowledge-base/", + "category": "memory", + "description": { + "en": "General-purpose knowledge base for DSH: import md/txt/json/yml/docx/pdf, folder management, FTS5 full-text search (BM25), and a Web management UI.", + "zh": "DSH 通用知识库插件:导入 md/txt/json/yml/docx/pdf、目录化管理、FTS5 全文检索(BM25)、Web 管理界面。" + }, + "npm": "dsh-knowledge-base", + "stars": 7, + "downloads": 1165, + "install": "dsh plugin --profile web add dsh-knowledge-base", + "added": "2026-08-16" + }, + { + "name": "dsh-hindsight-local-optimize", + "owner": "Hua1Q1nG", + "url": "https://github.com/Hua1Q1nG/dsh-hindsight-local-optimize", + "page": "https://awesome-dsh-plugin.com/p/Hua1Q1nG/dsh-hindsight-local-optimize/", + "category": "memory", + "description": { + "en": "Local-memory lifecycle for the official Hindsight plugin on DSH desktop: one-click start/stop of the hindsight-embed daemon, auto-start on boot, stop on exit, hides the uv.exe console window, and installs the vault/models to another drive.", + "zh": "为 DSH 桌面端官方 Hindsight 插件补齐本地记忆生命周期:一键启停 hindsight-embed 守护进程、开机自启、退出随停、隐藏 uv.exe 黑框,并支持把档案柜与模型装到其它盘。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Hua1Q1nG/dsh-hindsight-local-optimize", + "added": "2026-08-19" + }, + { + "name": "dsh-prompt-self", + "owner": "Hua1Q1nG", + "url": "https://github.com/Hua1Q1nG/dsh-prompt-self", + "page": "https://awesome-dsh-plugin.com/p/Hua1Q1nG/dsh-prompt-self/", + "category": "memory", + "description": { + "en": "Rewrites each request against a learned prompt profile and automatically updates habits, anti-hallucination rules, and learning records.", + "zh": "按学习到的 Prompt 画像改写每个请求,并自动更新习惯清单、防幻觉规则与学习记录。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Hua1Q1nG/dsh-prompt-self", + "added": "2026-08-17" + }, + { + "name": "dsh-file-memory", + "owner": "ICCuse", + "url": "https://github.com/ICCuse/dsh-file-memory", + "page": "https://awesome-dsh-plugin.com/p/ICCuse/dsh-file-memory/", + "category": "memory", + "description": { + "en": "File-backed working memory: memorize/recall key premises verbatim in a session notes file so they survive context compaction losslessly.", + "zh": "文件型工作记忆:memorize/recall 把关键前提逐字保存在会话笔记文件,无损挺过上下文压缩。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:ICCuse/dsh-file-memory", + "added": "2026-08-14" + }, + { + "name": "dsh-knowledge", + "owner": "ICCuse", + "url": "https://github.com/ICCuse/dsh-knowledge", + "page": "https://awesome-dsh-plugin.com/p/ICCuse/dsh-knowledge/", + "category": "memory", + "description": { + "en": "Bridge into a global Markdown knowledge base shared with the Codex kb.cmd CLI: kb_add/kb_search/kb_show/kb_timeline tools with byte-compatible frontmatter.", + "zh": "全局知识库桥:kb_add/kb_search/kb_show/kb_timeline 读写与 Codex 共享的 D:\\knowledge(格式逐字节兼容)。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:ICCuse/dsh-knowledge", + "added": "2026-08-14" + }, + { + "name": "dsh-premise-guard", + "owner": "ICCuse", + "url": "https://github.com/ICCuse/dsh-premise-guard", + "page": "https://awesome-dsh-plugin.com/p/ICCuse/dsh-premise-guard/", + "category": "memory", + "description": { + "en": "Post-compaction premise-drift guard: injects a one-shot notice when a compaction summary drops a critical literal anchor.", + "zh": "压缩后前提漂移守卫:摘要丢失关键字面锚点时注入一次性提醒。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:ICCuse/dsh-premise-guard", + "added": "2026-08-14" + }, + { + "name": "dsh-memory-spaces", + "owner": "icearia0219", + "url": "https://github.com/icearia0219/dsh-memory-spaces", + "page": "https://awesome-dsh-plugin.com/p/icearia0219/dsh-memory-spaces/", + "category": "memory", + "description": { + "en": "Human-governed cross-session memory spaces with independent source and consumer relationships, provenance, versioning, and pre-send injection review.", + "zh": "用户治理的跨会话记忆空间,提供独立的来源与使用关系、来源追踪、版本管理和发送前注入检查。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:icearia0219/dsh-memory-spaces", + "added": "2026-08-27", + "screenshots": [ + "https://raw.githubusercontent.com/icearia0219/dsh-memory-spaces/HEAD/assets/memory-space-memories.png", + "https://raw.githubusercontent.com/icearia0219/dsh-memory-spaces/HEAD/assets/memory-space-consumers.png", + "https://raw.githubusercontent.com/icearia0219/dsh-memory-spaces/HEAD/assets/history-import-command.png" + ] + }, + { + "name": "MisakaNet", + "owner": "Ikalus1988", + "url": "https://github.com/Ikalus1988/MisakaNet", + "page": "https://awesome-dsh-plugin.com/p/Ikalus1988/MisakaNet/", + "category": "memory", + "description": { + "en": "Failure-recovery memory: search and record failure-recovery lessons from real engineering sessions, with BM25 + semantic RAG retrieval and a lessons knowledge base.", + "zh": "失败恢复记忆库:从真实工程会话中搜索和记录失败恢复教训,支持 BM25 + 语义 RAG 检索和知识库管理。" + }, + "npm": null, + "stars": 430, + "downloads": null, + "install": "dsh plugin --profile web add github:Ikalus1988/MisakaNet", + "added": "2026-08-15" + }, + { + "name": "dsh-hermes-memory", + "owner": "isheng-eqi", + "url": "https://github.com/isheng-eqi/dsh-hermes-memory", + "page": "https://awesome-dsh-plugin.com/p/isheng-eqi/dsh-hermes-memory/", + "category": "memory", + "description": { + "en": "Faithful port of the hermes-agent MEMORY.md / USER.md mechanism for DSH: bounded dual-bank memory (2200/1375 chars), a single `memory` tool (add/replace/remove/batch), frozen-snapshot injection per session, nudge reminders, DSH-native JSON storage — zero external dependencies, zero LLM cost.", + "zh": "DSH 上 hermes-agent 记忆机制的忠实移植(MEMORY.md / USER.md):有界双记忆库(2200/1375 字符)、单个 memory 工具(add/replace/remove/batch 全有或全无)、每会话冻结快照注入与 nudge 提醒、DSH 原生 JSON 存储——零外部依赖、零 LLM 成本。" + }, + "npm": "dsh-hermes-memory", + "stars": 5, + "downloads": 289, + "install": "dsh plugin --profile web add dsh-hermes-memory", + "added": "2026-08-17" + }, + { + "name": "dsh-cue-bank", + "owner": "itr-del", + "url": "https://github.com/itr-del/dsh-cue-bank", + "page": "https://awesome-dsh-plugin.com/p/itr-del/dsh-cue-bank/", + "category": "memory", + "description": { + "en": "Cross-session event-cue memory for DeepSeek Harness: auto-extracts task/conversation cues and user language habits from every turn into a persistent global cue bank, then re-injects remembered context on topic-switch detection (keyword or vector matching).", + "zh": "跨会话「触点记忆库」插件:自动从每轮对话提取任务/会话级触点与用户惯用词,存入全局持久记忆库;检测到话题切换时重新注入相关历史记忆(关键词或向量匹配)。" + }, + "npm": "dsh-cue-bank", + "stars": 2, + "downloads": 173, + "install": "dsh plugin --profile web add dsh-cue-bank", + "added": "2026-08-18" + }, + { + "name": "dsh-memory", + "owner": "Jesse-njx", + "url": "https://github.com/Jesse-njx/dsh-memory", + "page": "https://awesome-dsh-plugin.com/p/Jesse-njx/dsh-memory/", + "category": "memory", + "description": { + "en": "Cited memory over DSH's lossless session log: distilled facts carry `(sessionId, eventRange)` citations that expand back to the exact original log excerpt.", + "zh": "基于 DSH 无损会话日志的引用式记忆:蒸馏出的事实带 `(sessionId, eventRange)` 引用,可随时展开回原始日志片段。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Jesse-njx/dsh-memory", + "added": "2026-08-14" + }, + { + "name": "dsh-memoria", + "owner": "jiayan-xu", + "url": "https://github.com/jiayan-xu/dsh-memoria", + "page": "https://awesome-dsh-plugin.com/p/jiayan-xu/dsh-memoria/", + "category": "memory", + "description": { + "en": "Vector + graph memory backend: observe/remember/search/recall tools against a local memoria server, fusing HNSW semantic recall, FTS5 keyword search, and knowledge-graph signals via RRF ranking, with automatic turn-end persistence and per-agent namespace isolation.", + "zh": "向量+图记忆后端:observe/remember/search/recall 四个工具对接本地 memoria 服务,HNSW 语义召回 + FTS5 关键词 + 知识图谱信号经 RRF 融合排序,回合结束自动写入,按 Agent 命名空间隔离。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:jiayan-xu/dsh-memoria", + "added": "2026-08-14" + }, + { + "name": "dsh-memoria-extra", + "owner": "jiayan-xu", + "url": "https://github.com/jiayan-xu/dsh-memoria-extra", + "page": "https://awesome-dsh-plugin.com/p/jiayan-xu/dsh-memoria-extra/", + "category": "memory", + "description": { + "en": "Advanced memoria tools: session context injection (profile + recall), recent decisions, health report, namespace allowlist, memory relation graph, and entity search; companion to dsh-memoria.", + "zh": "memoria 进阶工具:会话上下文注入(画像+召回)、近期决策、健康报告、命名空间白名单、记忆关系图与实体搜索;dsh-memoria 的伴生插件。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:jiayan-xu/dsh-memoria-extra", + "added": "2026-08-14" + }, + { + "name": "deepseek-harness-forge-plugins#forge-memory", + "owner": "jinguanghai", + "url": "https://github.com/jinguanghai/deepseek-harness-forge-plugins/tree/main/plugins/forge-memory", + "page": "https://awesome-dsh-plugin.com/p/jinguanghai/deepseek-harness-forge-plugins--plugins-forge-memory/", + "category": "memory", + "description": { + "en": "BM25 keyword-based memory recall.", + "zh": "基于 BM25 关键词检索的记忆召回。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:jinguanghai/deepseek-harness-forge-plugins#path:/plugins/forge-memory", + "added": "2026-08-14" + }, + { + "name": "dsh-memories", + "owner": "jisi71", + "url": "https://github.com/jisi71/dsh-memories", + "page": "https://awesome-dsh-plugin.com/p/jisi71/dsh-memories/", + "category": "memory", + "description": { + "en": "Dual-ledger cross-session memory: auto-extracts lasting facts from conversations into MEMORY.md and maintains a PROGRESS.md project ledger, recalling both into every new session.", + "zh": "双账本跨会话记忆:自动把对话中的长期事实提取进 MEMORY.md,并维护项目进度账本 PROGRESS.md,每个新会话自动召回两本账。" + }, + "npm": "dsh-memories", + "stars": 1, + "downloads": 1483, + "install": "dsh plugin --profile web add dsh-memories", + "added": "2026-08-25" + }, + { + "name": "context-pruner", + "owner": "JohnXu22786", + "url": "https://github.com/JohnXu22786/context-pruner", + "page": "https://awesome-dsh-plugin.com/p/JohnXu22786/context-pruner/", + "category": "memory", + "description": { + "en": "Session context triage for long DeepSeek Harness sessions: deterministic screeners trim stale, duplicate, failed, and oversized content through the official ctx.compaction seam.", + "zh": "面向 DeepSeek Harness 长会话的上下文分诊插件:通过官方 ctx.compaction 接缝,用确定性规则筛查并裁剪过期、重复、失败与超大的上下文内容。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:JohnXu22786/context-pruner", + "added": "2026-08-16" + }, + { + "name": "memory-standard", + "owner": "JohnXu22786", + "url": "https://github.com/JohnXu22786/memory-standard", + "page": "https://awesome-dsh-plugin.com/p/JohnXu22786/memory-standard/", + "category": "memory", + "description": { + "en": "Memory Standard Protocol (mm) for DeepSeek Harness: layered MEMORY.md with hard budgets, frozen snapshots, mm:// URI interop, session-log digestion, mem_* tools and a standalone CLI.", + "zh": "DeepSeek Harness 记忆标准协议(mm):分层 MEMORY.md 与硬性预算、冻结快照、mm:// URI 互认、会话日志摄取,附带 mem_* 工具与独立 CLI。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:JohnXu22786/memory-standard", + "added": "2026-08-23" + }, + { + "name": "dsh-layered-memory", + "owner": "JunNanLYS", + "url": "https://github.com/JunNanLYS/dsh-layered-memory", + "page": "https://awesome-dsh-plugin.com/p/JunNanLYS/dsh-layered-memory/", + "category": "memory", + "description": { + "en": "Long-term memory for DSH: conversations auto-distilled into atomic facts, scene summaries and a persona profile, with relevant memories injected before every model step. Zero-config out of the box — hybrid BM25 + vector retrieval, optional offline local embeddings, chat/work family separation.", + "zh": "dsh 长期记忆:对话自动蒸馏为原子事实、场景摘要与用户画像,每步模型调用前自动注入相关记忆。开箱零配置——BM25 + 向量混合检索,可选本地离线嵌入,chat/work 双族隔离。" + }, + "npm": "dsh-layered-memory", + "stars": 9, + "downloads": 2535, + "install": "dsh plugin --profile web add dsh-layered-memory", + "added": "2026-08-18" + }, + { + "name": "dsh-biomemory", + "owner": "KLRSL", + "url": "https://github.com/KLRSL/dsh-biomemory", + "page": "https://awesome-dsh-plugin.com/p/KLRSL/dsh-biomemory/", + "category": "memory", + "description": { + "en": "Biomimetic memory system for DeepSeek Harness: plain-Markdown data layer, memory tools, frozen-snapshot injection, graded approval gating, structured audits, memory metabolism (dream), memory pins, semantic recall, /memory command, and cross-session retrieval.", + "zh": "为 DeepSeek Harness 打造的生物仿生记忆系统:纯 Markdown 数据层、memory 工具、冻结快照注入、分级审批门、结构化审计、记忆代谢(dream)、记忆钉、语义检索、/memory 命令与跨会话召回。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:KLRSL/dsh-biomemory", + "added": "2026-08-16" + }, + { + "name": "dsh-project", + "owner": "lanyun077", + "url": "https://github.com/lanyun077/dsh-project", + "page": "https://awesome-dsh-plugin.com/p/lanyun077/dsh-project/", + "category": "memory", + "description": { + "en": "Organize folders and sessions into projects with shared PROJECT.md memory and AGENTS.md instructions injected into every session, plus a project tree sidebar.", + "zh": "把多个文件夹与会话收纳为「项目」,共享 PROJECT.md 记忆与 AGENTS.md 指令并注入每个会话,附带项目树侧边栏。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:lanyun077/dsh-project", + "added": "2026-08-16" + }, + { + "name": "dsh-knowledge", + "owner": "lemoncat7", + "url": "https://github.com/lemoncat7/dsh-knowledge", + "page": "https://awesome-dsh-plugin.com/p/lemoncat7/dsh-knowledge/", + "category": "memory", + "description": { + "en": "Provides local and remote knowledge bases with project- and session-scoped recall, controlled write-back, and an embedded Web management console.", + "zh": "提供本地与远程知识库,支持项目和会话范围检索、可控回写以及内嵌 Web 管理台。" + }, + "npm": "@lemoncat7/dsh-knowledge", + "stars": 4, + "downloads": 2138, + "install": "dsh plugin --profile web add @lemoncat7/dsh-knowledge", + "added": "2026-08-18" + }, + { + "name": "dsh-plugin-memory", + "owner": "LittleBlackTong", + "url": "https://github.com/LittleBlackTong/dsh-plugin-memory", + "page": "https://awesome-dsh-plugin.com/p/LittleBlackTong/dsh-plugin-memory/", + "category": "memory", + "description": { + "en": "Long-term cross-session markdown memory with an LLM-Wiki structure and a SOUL.md persona, hard-injected at every session start, plus remember/recall/consolidate/forget workflows, an embedded memory skill, pack/unpack migration tooling, and an idle-time first-person recall nudge that surfaces real memories in a companion voice.", + "zh": "带 LLM Wiki 结构与 SOUL 人格文件的长期 markdown 记忆库:会话启动强制注入 boot 块,含 remember/recall/consolidate/forget 工作流、内嵌 memory 技能、pack/unpack 迁移工具,以及空闲时以第一人称主动追忆(recall nudge)的拟人化能力。" + }, + "npm": "dsh-plugin-memory", + "stars": 3, + "downloads": 1501, + "install": "dsh plugin --profile web add dsh-plugin-memory", + "added": "2026-08-19" + }, + { + "name": "dsh-compaction", + "owner": "ljsysfurryACE", + "url": "https://github.com/ljsysfurryACE/dsh-compaction", + "page": "https://awesome-dsh-plugin.com/p/ljsysfurryACE/dsh-compaction/", + "category": "memory", + "description": { + "en": "Compaction backend replacing LLM summarization with a deterministic semantic extractor (keeps code/paths/commands, drops chatter) plus 28.4x KV-compression accounting.", + "zh": "压缩后端:用确定性语义提取器替换 LLM 摘要(保留代码/路径/命令,剔除闲聊),附带 28.4x KV 压缩记账。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:ljsysfurryACE/dsh-compaction", + "added": "2026-08-15" + }, + { + "name": "dsh-memory-director", + "owner": "ljsysfurryACE", + "url": "https://github.com/ljsysfurryACE/dsh-memory-director", + "page": "https://awesome-dsh-plugin.com/p/ljsysfurryACE/dsh-memory-director/", + "category": "memory", + "description": { + "en": "LLM-driven remember/forget memory: after each turn a model decides what to keep (MemoryDirector), injects relevant memories before each step, dedups, and persists cross-session.", + "zh": "LLM 驱动的记忆记/忘插件:每轮对话后由模型决定记住什么/忘记什么(MemoryDirector),请求前注入相关记忆,自动去重,跨会话持久化。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:ljsysfurryACE/dsh-memory-director", + "added": "2026-08-15" + }, + { + "name": "distill", + "owner": "LoserFox", + "url": "https://github.com/LoserFox/distill", + "page": "https://awesome-dsh-plugin.com/p/LoserFox/distill/", + "category": "memory", + "description": { + "en": "Automatic conversation distillation: background subagent reflection + skill create/update.", + "zh": "自动对话蒸馏:后台 subagent 反省 + 技能 create/update。" + }, + "npm": null, + "stars": 23, + "downloads": null, + "install": "dsh plugin --profile web add github:LoserFox/distill", + "added": "2026-08-13" + }, + { + "name": "dsh-memory-palace", + "owner": "lovezi0", + "url": "https://github.com/lovezi0/dsh-memory-palace", + "page": "https://awesome-dsh-plugin.com/p/lovezi0/dsh-memory-palace/", + "category": "memory", + "description": { + "en": "Human-readable Markdown memory for DeepSeek Harness: user-level (~/.deepseek-harness/MEMORY.md) + workspace-level memory injected every turn, auto daily-log writing with distillation, bridges existing WorkBuddy/CodeBuddy memory dirs, memory_note / memory_note_user / memory_read tools with built-in dedup, and a bilingual settings page. Plain-text storage, no database.", + "zh": "人类可读的 Markdown 记忆插件:用户级(~/.deepseek-harness/MEMORY.md)+ 工作区级双层记忆,每轮自动注入提示词,回合结束自动写日志并按期蒸馏沉淀;可桥接已有 WorkBuddy/CodeBuddy 记忆目录;内置 memory_note / memory_note_user / memory_read 三个工具(自动去重)与中英双语设置页。纯文件存储、无数据库,记事本即可编辑。" + }, + "npm": "dsh-memory-palace", + "stars": 3, + "downloads": 478, + "install": "dsh plugin --profile web add dsh-memory-palace", + "added": "2026-08-16" + }, + { + "name": "dsh-plugin-context-manager", + "owner": "luxiwusuobuneng", + "url": "https://github.com/luxiwusuobuneng/dsh-plugin-context-manager", + "page": "https://awesome-dsh-plugin.com/p/luxiwusuobuneng/dsh-plugin-context-manager/", + "category": "memory", + "description": { + "en": "Context manager for DeepSeek Harness: auto-summarizes every turn into priority-sorted records, injects them plus pinned cross-session notes and custom text into each model request, and folds any-size real conversation ranges into summaries to save tokens. Persistent and restart-safe. NOTE: manual multi-step install — copy the three package folders into profiles/node_modules and add the provided cordis.patch.yml rows, then restart DSH; not a standard one-command install.", + "zh": "DeepSeek Harness 上下文管理:每轮对话自动总结为记录并按优先级排序注入,⭐ 置顶记录跨会话继承,自定义文本每轮注入;真实对话任意范围折叠成摘要真省 token,全状态持久化重启不丢。注意:非标准一键安装——需手动将三个包目录复制到 profiles/node_modules、粘贴 cordis.patch.yml 接线并重启 DSH。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:luxiwusuobuneng/dsh-plugin-context-manager", + "added": "2026-08-28" + }, + { + "name": "dsh-self-improved", + "owner": "madage", + "url": "https://github.com/madage/dsh-self-improved", + "page": "https://awesome-dsh-plugin.com/p/madage/dsh-self-improved/", + "category": "memory", + "description": { + "en": "Long-term memory & self-evolving plugin for DSH: L0 capture → L1 memory extraction → L2 scene grouping → L3 user persona, auto recall injection, skill synthesis, fully local (SQLite FTS5 + jieba, optional vector recall).", + "zh": "DeepSeek Harness 长期记忆与自进化插件:L0 对话捕获 → L1 记忆提取 → L2 场景归纳 → L3 用户画像,自动召回注入 + 技能合成,纯本地(SQLite FTS5 + jieba,可选向量召回)。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:madage/dsh-self-improved", + "added": "2026-08-16" + }, + { + "name": "dsh-memory", + "owner": "Max-Null", + "url": "https://github.com/Max-Null/dsh-memory", + "page": "https://awesome-dsh-plugin.com/p/Max-Null/dsh-memory/", + "category": "memory", + "description": { + "en": "Cross-session plaintext memory: deterministic BM25 keyword recall (no embeddings), memory_save/list/search/confirm/forget tools behind a human-confirm gate, and global + project-scoped JSON stores that follow your git repo.", + "zh": "跨会话明文记忆:确定性 BM25 关键词召回(无向量嵌入),memory_save/list/search/confirm/forget 五个工具 + 人工确认闸门,global/project 两层 JSON 存储(project 随 git 分享)。" + }, + "npm": "@max-null/dsh-memory", + "stars": 3, + "downloads": 2337, + "install": "dsh plugin --profile web add @max-null/dsh-memory", + "added": "2026-08-16" + }, + { + "name": "opencontext#dsh-opencontext", + "owner": "melandlabs", + "url": "https://github.com/melandlabs/opencontext/tree/main/plugins/dsh-opencontext", + "page": "https://awesome-dsh-plugin.com/p/melandlabs/opencontext--plugins-dsh-opencontext/", + "category": "memory", + "description": { + "en": "DeepSeek Harness integration for OpenContext with durable cross-session memory, automatic prompt capture and recall, session summaries, structured insights, and document retrieval.", + "zh": "为 DeepSeek Harness 接入 OpenContext,提供跨会话持久记忆、提示词自动捕获与召回、会话摘要、结构化洞察和文档检索。" + }, + "npm": "dsh-opencontext", + "stars": 52, + "downloads": 3104, + "install": "dsh plugin --profile web add dsh-opencontext", + "added": "2026-08-24" + }, + { + "name": "dsh-patchouli", + "owner": "memorax-ai", + "url": "https://github.com/memorax-ai/dsh-patchouli", + "page": "https://awesome-dsh-plugin.com/p/memorax-ai/dsh-patchouli/", + "category": "memory", + "description": { + "en": "DeepSeek Harness memory and knowledge hub that routes update, retrieve, and subscribe calls across compatible plugins, with an optional transactional Rust backend.", + "zh": "面向 DeepSeek Harness 的记忆与知识中台:在兼容插件之间路由 update、retrieve 和 subscribe 调用,并提供可选的事务化 Rust 后端。" + }, + "npm": "dsh-patchouli", + "stars": 2, + "downloads": 881, + "install": "dsh plugin --profile web add dsh-patchouli", + "added": "2026-08-18" + }, + { + "name": "memorylake-harness#dsh-plugin", + "owner": "memorylake-ai", + "url": "https://github.com/memorylake-ai/memorylake-harness/tree/main/dsh-plugin", + "page": "https://awesome-dsh-plugin.com/p/memorylake-ai/memorylake-harness--dsh-plugin/", + "category": "memory", + "description": { + "en": "Memory Lake as a persistent memory layer for dsh: memory_search, memory_remember, and memory_forget tools over the memorylake CLI, a session status line, and guided setup/diagnostic skills, sharing one ~/.memorylake identity and memory set with the Claude Code and Codex plugins.", + "zh": "将 Memory Lake 作为 dsh 的持久记忆层:基于 memorylake CLI 的 memory_search、memory_remember、memory_forget 三个工具,会话状态行,以及引导安装/诊断 skills,与 Claude Code、Codex 插件共享同一 ~/.memorylake 身份与记忆。" + }, + "npm": "@memorylake/dsh-plugin", + "stars": 3, + "downloads": 202, + "install": "dsh plugin --profile web add @memorylake/dsh-plugin", + "added": "2026-08-17" + }, + { + "name": "dsh-trilium", + "owner": "MineRPi", + "url": "https://github.com/MineRPi/dsh-trilium", + "page": "https://awesome-dsh-plugin.com/p/MineRPi/dsh-trilium/", + "category": "memory", + "description": { + "en": "Connect Trilium notes to the DSH agent via ETAPI with 20 agent tools: durable memory (remember/recall + auto-injected index), note management (CRUD/clone/attributes/revisions), full-text search, weekly-report workflow, attachments, calendar notes, backup/import, and a settings card.", + "zh": "通过 ETAPI 把 Trilium 笔记库接入 agent,提供 20 个工具:记忆读写(remember/recall + 自动注入索引)、笔记管理(CRUD/克隆/属性/修订)、全文搜索、周报工作流、附件、日历笔记、备份导入,独立设置卡片,配置 0600 落盘。" + }, + "npm": "dsh-trilium", + "stars": 1, + "downloads": 488, + "install": "dsh plugin --profile web add dsh-trilium", + "added": "2026-08-18" + }, + { + "name": "dsh-obsidian", + "owner": "mingzeng21", + "url": "https://github.com/mingzeng21/dsh-obsidian", + "page": "https://awesome-dsh-plugin.com/p/mingzeng21/dsh-obsidian/", + "category": "memory", + "description": { + "en": "Connect dsh to a local Obsidian vault: search, read, write, move, and trash notes through `obsidian_*` tools.", + "zh": "把 dsh 连接到本地 Obsidian vault:通过 `obsidian_*` 工具搜索、读取、写入、移动与回收笔记。" + }, + "npm": "dsh-obsidian", + "stars": 10, + "downloads": 1149, + "install": "dsh plugin --profile web add dsh-obsidian", + "added": "2026-08-16" + }, + { + "name": "dsh-mneme", + "owner": "modusensus", + "url": "https://github.com/modusensus/dsh-mneme", + "page": "https://awesome-dsh-plugin.com/p/modusensus/dsh-mneme/", + "category": "memory", + "description": { + "en": "Self-evolving AI memory engine with heat-powered decay (power-law forgetting), sleep consolidation, entity heat projection, and offline semantic search — memories grow, cool down, and fade just like a real brain.", + "zh": "AI 自进化记忆引擎:heat 热度模型驱动的记忆遗忘与巩固、sleep 深度维护、实体热投影、离线语义搜索 — 让 AI 的记忆像人脑一样会生长、会淡忘。" + }, + "npm": "@modusensus/dsh-mneme", + "stars": 50, + "downloads": 10599, + "install": "dsh plugin --profile web add @modusensus/dsh-mneme", + "added": "2026-08-14" + }, + { + "name": "dsh-memory-manager", + "owner": "MoonlitDropOfBlood", + "url": "https://github.com/MoonlitDropOfBlood/dsh-memory-manager", + "page": "https://awesome-dsh-plugin.com/p/MoonlitDropOfBlood/dsh-memory-manager/", + "category": "memory", + "description": { + "en": "ZCode-style persistent project memory with a full settings manager, index-first injection and agent memory tools.", + "zh": "ZCode 风格持久化项目记忆:设置内完整管理页、索引式注入与 agent 记忆工具。" + }, + "npm": "@duke-dsh-plugins/dsh-memory-manager", + "stars": 1, + "downloads": 573, + "install": "dsh plugin --profile web add @duke-dsh-plugins/dsh-memory-manager", + "added": "2026-08-23" + }, + { + "name": "DeepSeek-Harness-Hanako-Memory", + "owner": "moononnn", + "url": "https://github.com/moononnn/DeepSeek-Harness-Hanako-Memory", + "page": "https://awesome-dsh-plugin.com/p/moononnn/DeepSeek-Harness-Hanako-Memory/", + "category": "memory", + "description": { + "en": "Hana-style assistant manager for DSH: visual preset management (cards, yuan personalities, avatar cropping) plus a full Hana memory stack (rolling summaries, layered compilation, SQLite fact store, deep memory).", + "zh": "Hana 式助手管理:可视化配置助手预设(卡片、四性格「元」、头像裁剪),并完整复刻 Hana 记忆系统(滚动摘要、分层编译、SQLite 事实库、深度记忆)。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:moononnn/DeepSeek-Harness-Hanako-Memory", + "added": "2026-08-16" + }, + { + "name": "dsh-honcho-sync", + "owner": "nanpaidashi", + "url": "https://github.com/nanpaidashi/dsh-honcho-sync", + "page": "https://awesome-dsh-plugin.com/p/nanpaidashi/dsh-honcho-sync/", + "category": "memory", + "description": { + "en": "Honcho memory bridge: auto-syncs every conversation turn to a self-hosted Honcho service, with recall/search/remember/context/status tools, context injection on session start, and a visual settings panel.", + "zh": "Honcho 记忆桥接:每轮对话自动同步到自建 Honcho 记忆服务(NAS/服务器/云),提供 recall/search/remember/context/status 工具,会话启动自动注入上下文,内置可视化设置面板。" + }, + "npm": "@nanpaidashi/dsh-honcho-sync", + "stars": 6, + "downloads": 1109, + "install": "dsh plugin --profile web add @nanpaidashi/dsh-honcho-sync", + "added": "2026-08-16" + }, + { + "name": "dsh-plugin-memory", + "owner": "NattoCB", + "url": "https://github.com/NattoCB/dsh-plugin-memory", + "page": "https://awesome-dsh-plugin.com/p/NattoCB/dsh-plugin-memory/", + "category": "memory", + "description": { + "en": "Persistent five-layer memory system with relevance injection, LLM auto-extraction, profile rotation, and six agent tools.", + "zh": "五层持久记忆系统:相关性注入、LLM 自动提取、画像轮替与六个 agent 工具。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:NattoCB/dsh-plugin-memory", + "added": "2026-08-21" + }, + { + "name": "unified-agent-memory", + "owner": "Noelune", + "url": "https://github.com/Noelune/unified-agent-memory", + "page": "https://awesome-dsh-plugin.com/p/Noelune/unified-agent-memory/", + "category": "memory", + "description": { + "en": "One shared Obsidian vault for every agent: dependency-free Python core (search/promote/adjudicate/forget), vault template, dsh plugin (memory_search/show/submit/status).", + "zh": "多 Agent 共享一个 Obsidian vault:零依赖 Python core(检索/晋升/裁决/遗忘)+ vault 模板 + dsh 插件(memory_search/show/submit/status)。" + }, + "npm": "dsh-unified-agent-memory", + "stars": 5, + "downloads": 780, + "install": "dsh plugin --profile web add dsh-unified-agent-memory", + "added": "2026-08-14" + }, + { + "name": "nowledge-mem-deepseek-harness", + "owner": "nowledge-co", + "url": "https://github.com/nowledge-co/nowledge-mem-deepseek-harness", + "page": "https://awesome-dsh-plugin.com/p/nowledge-co/nowledge-mem-deepseek-harness/", + "category": "memory", + "description": { + "en": "One memory layer for every AI tool and agent: Context Bundle injection, prompt-time recall, MCP tools, and turn-end DSH thread capture.", + "zh": "给所有 AI 工具和 Agent 共用的一层记忆:注入 Context Bundle、提示时检索、MCP 工具与回合结束 DSH 线程捕获。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:nowledge-co/nowledge-mem-deepseek-harness", + "added": "2026-08-14" + }, + { + "name": "dsh-kb-sieve", + "owner": "omdsh-dev", + "url": "https://github.com/omdsh-dev/dsh-kb-sieve", + "page": "https://awesome-dsh-plugin.com/p/omdsh-dev/dsh-kb-sieve/", + "category": "memory", + "description": { + "en": "Build auditable KB packs (SQLite FTS5) from md/txt/docx/pdf with deterministic retrieval and original-text reading.", + "zh": "从 md/txt/docx/pdf 构建可审计知识库包(SQLite FTS5),确定性检索与原文阅读。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:omdsh-dev/dsh-kb-sieve", + "added": "2026-08-13" + }, + { + "name": "dsh-mnemon", + "owner": "omdsh-dev", + "url": "https://github.com/omdsh-dev/dsh-mnemon", + "page": "https://awesome-dsh-plugin.com/p/omdsh-dev/dsh-mnemon/", + "category": "memory", + "description": { + "en": "Cross-agent, local-first persistent memory plugin for DeepSeek Harness (DSH), powered by Mnemon. It shares long-term memory across Mnemon-enabled agents and adds runtime memory, searchable project documents, semantic recall, knowledge graph, and a Sidebar UI.", + "zh": "由 Mnemon 驱动的 DeepSeek Harness(DSH)跨 Agent、本地优先的持久记忆插件。它可在支持 Mnemon 的 Agent 之间共享长期记忆,并提供运行时记忆、可检索项目档案、语义召回、知识图谱和 Sidebar UI。" + }, + "npm": "dsh-mnemon", + "stars": 222, + "downloads": 15675, + "install": "dsh plugin --profile web add dsh-mnemon", + "added": "2026-08-13" + }, + { + "name": "dsh-mem0", + "owner": "orangeshinee", + "url": "https://github.com/orangeshinee/dsh-mem0", + "page": "https://awesome-dsh-plugin.com/p/orangeshinee/dsh-mem0/", + "category": "memory", + "description": { + "en": "Agent tools for a self-hosted mem0 server: add, search, get, update, delete, history, reset and status memories, with a settings-panel configuration card.", + "zh": "自托管 mem0 记忆读写插件:agent 直接读写自有 mem0 服务(增删查改、搜索、历史、重置、状态),并带设置面板配置卡片。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:orangeshinee/dsh-mem0", + "added": "2026-08-17" + }, + { + "name": "dsh-library", + "owner": "PerryLink", + "url": "https://github.com/PerryLink/dsh-library", + "page": "https://awesome-dsh-plugin.com/p/PerryLink/dsh-library/", + "category": "memory", + "description": { + "en": "Turns local markdown and text documents into a queryable knowledge base with hybrid semantic and keyword search, citation verification, and source injection.", + "zh": "将本地 Markdown 与文本文档转为可检索的知识库,支持语义与关键词混合搜索、引用校验与来源注入。" + }, + "npm": "dsh-library", + "stars": 3, + "downloads": 1291, + "install": "dsh plugin --profile web add dsh-library", + "added": "2026-08-18" + }, + { + "name": "dsh-memento", + "owner": "PerryLink", + "url": "https://github.com/PerryLink/dsh-memento", + "page": "https://awesome-dsh-plugin.com/p/PerryLink/dsh-memento/", + "category": "memory", + "description": { + "en": "Bounded, layered, approval-gated, auditable cross-session memory: a typed `ctx.memory` seam with a zero-dependency SQLite provider, a `memory` tool, and frozen snapshot injection, plus the dsh-memory-protocol v1 rehearsal with an adapter registry and a distributable conformance suite.", + "zh": "有界、分层、带审批门、可审计的跨会话记忆:`ctx.memory` 服务 + 零依赖 SQLite 存储 + `memory` 工具与冻结快照注入,并预演 dsh-memory-protocol v1——适配器注册表与可分发的一致性套件。" + }, + "npm": "dsh-memento", + "stars": 62, + "downloads": 2223, + "install": "dsh plugin --profile web add dsh-memento", + "added": "2026-08-14" + }, + { + "name": "dsh-meow-memory", + "owner": "Phant0Meow", + "url": "https://github.com/Phant0Meow/dsh-meow-memory", + "page": "https://awesome-dsh-plugin.com/p/Phant0Meow/dsh-meow-memory/", + "category": "memory", + "description": { + "en": "Project-scoped cross-session memory: seven-layer SQLite store (soul/user/project/fact/lesson/rules/topic) on node:sqlite, cache-friendly first-message injection plus per-message keyword hits, memory_remember/search/find_similar/read/update/project tools, and idle-window consolidation (\"dream\") with peak-hour suppression. Prompt texts ship as per-language data files — the framework supports multilingual users.", + "zh": "项目级跨会话记忆:node:sqlite 七层存储(soul/user/project/fact/lesson/rules/topic),首条消息缓存友好注入 + 每条消息关键词命中,memory_remember/search/find_similar/read/update/project 工具,空闲窗口自动整理记忆(dream,峰时抑制)。prompt 文案按语言数据文件加载——框架支持多语言用户。" + }, + "npm": "meow-memory", + "stars": 44, + "downloads": 2873, + "install": "dsh plugin --profile web add meow-memory", + "added": "2026-08-14" + }, + { + "name": "dsh-plugin", + "owner": "plur-ai", + "url": "https://github.com/plur-ai/dsh-plugin", + "page": "https://awesome-dsh-plugin.com/p/plur-ai/dsh-plugin/", + "category": "memory", + "description": { + "en": "PLUR memory rendered into the system prompt on each assembly rather than exposed as a tool call, so the block is replaced instead of appended and context stays flat across a session. Fully local hybrid search (BM25 + BGE fused with RRF), plain YAML storage you can edit, per-workspace scoping, and a /plur-memory browser.", + "zh": "PLUR 记忆在每次组装时渲染进系统提示词,而非以工具调用形式暴露,因此记忆块是被替换而不是追加,整段会话的上下文长度保持不变。全本地混合检索(BM25 + BGE,RRF 融合)、可直接编辑的纯 YAML 存储、按工作区划分 scope,并提供 /plur-memory 浏览器界面。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:plur-ai/dsh-plugin", + "added": "2026-08-15" + }, + { + "name": "dsh-memoir", + "owner": "Qinling-Melon-Farmers", + "url": "https://github.com/Qinling-Melon-Farmers/dsh-memoir", + "page": "https://awesome-dsh-plugin.com/p/Qinling-Melon-Farmers/dsh-memoir/", + "category": "memory", + "description": { + "en": "Local-only project memory with zero bundled runtime dependencies or external services: structured JSON plus generated PROJECT_MEMORY.md, token-bounded session-frozen Hot Memory designed to preserve prompt-prefix cache hits, cached cross-project BM25 ranked recall, editable pin/supersede/archive lifecycle, configurable top-level turn-end distillation, and a bilingual Web panel for search, inspection, diagnostics, and live settings.", + "zh": "仅本地运行、零额外运行时依赖(仅使用 DSH 宿主 peer)且不调用外部服务的项目记忆:结构化 JSON 与自动生成的 PROJECT_MEMORY.md、按 token 有界并按会话冻结的 Hot Memory 以优化 prompt-prefix cache 命中、带查询缓存的跨项目 BM25 排序召回、可编辑/置顶/supersede/归档生命周期、可配置的顶层回合自动蒸馏,以及支持搜索、检查、诊断和实时设置的中英双语 Web 面板。" + }, + "npm": "dsh-memoir", + "stars": 22, + "downloads": 1411, + "install": "dsh plugin --profile web add dsh-memoir", + "added": "2026-08-23" + }, + { + "name": "dsh-simple-wiki-memory", + "owner": "rainow", + "url": "https://github.com/rainow/dsh-simple-wiki-memory", + "page": "https://awesome-dsh-plugin.com/p/rainow/dsh-simple-wiki-memory/", + "category": "memory", + "description": { + "en": "A super-simplified LLM-wiki memory plugin: one index document (auto-loaded) + one markdown file per topic (read only when needed) — no dumping everything into the context and burning tokens. Simple and lightweight, painless to install/uninstall, and freely editable however you like.", + "zh": "超级简化版 llm-wiki 记忆插件:一个索引文档(自动加载)+ 每个主题一个 md 文件(需要时才读)——不会把所有东西塞进上下文烧 token。简单轻量,安装卸载无压力,想怎么改就怎么改。" + }, + "npm": "dsh-simple-wiki-memory", + "stars": 3, + "downloads": 0, + "install": "dsh plugin --profile web add dsh-simple-wiki-memory", + "added": "2026-08-19" + }, + { + "name": "l123-harness", + "owner": "reatcat", + "url": "https://github.com/reatcat/l123-harness", + "page": "https://awesome-dsh-plugin.com/p/reatcat/l123-harness/", + "category": "memory", + "description": { + "en": "Three-tier agent memory harness: L1 always-on CLAUDE.md, L2 index, L3 detail skeleton with templates, a gate that blocks writes to protected files without a one-time approval token, zero-judgement event logging, weekly distillation, and a TDD execution-loop skill.", + "zh": "三级记忆 agent 底座:L1 常驻层、L2 索引、L3 详情骨架与模板,受保护文件门禁(一次性审批令牌),零判断事件日志,周审提炼与 TDD 执行流技能。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:reatcat/l123-harness", + "added": "2026-08-23" + }, + { + "name": "dsh-mnemosyne", + "owner": "rebron1900", + "url": "https://github.com/rebron1900/dsh-mnemosyne", + "page": "https://awesome-dsh-plugin.com/p/rebron1900/dsh-mnemosyne/", + "category": "memory", + "description": { + "en": "Mnemosyne memory for DeepSeek Harness: remember / recall / forget / stats / sleep tools, embedded skill, auto-setup, optional auto-sync/prefetch, and a Settings panel. Data lives under ~/.dsh/mnemosyne.", + "zh": "Mnemosyne 记忆插件 for DeepSeek Harness:提供 remember / recall / forget / stats / sleep 工具、内嵌技能、Settings 面板和可选的自动记忆功能。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:rebron1900/dsh-mnemosyne", + "added": "2026-08-25" + }, + { + "name": "dsh-recall", + "owner": "Relistencode", + "url": "https://github.com/Relistencode/dsh-recall", + "page": "https://awesome-dsh-plugin.com/p/Relistencode/dsh-recall/", + "category": "memory", + "description": { + "en": "Conversation history recall for DSH: three-layer (literal/fuzzy/semantic) retrieval over every past session's original text, fully local & offline — AI never forgets what you told it. One-command install via `dsh.bundle.patch`, semantic layer runs in a worker thread.", + "zh": "对话历史回忆:字面/模糊/语义三层检索全部历史会话的原始文本,完全本地离线——AI 再也不会忘记你说的话。一条命令安装(自带 `dsh.bundle.patch`),语义推理跑在 worker 线程。" + }, + "npm": "dsh-recall", + "stars": 3, + "downloads": 1459, + "install": "dsh plugin --profile web add dsh-recall", + "added": "2026-08-16" + }, + { + "name": "TMCRA-Agent-Memory#deepseek-harness-local", + "owner": "reshuibuduo", + "url": "https://github.com/reshuibuduo/TMCRA-Agent-Memory/tree/main/integrations/deepseek-harness-local", + "page": "https://awesome-dsh-plugin.com/p/reshuibuduo/TMCRA-Agent-Memory--integrations-deepseek-harness-local/", + "category": "memory", + "description": { + "en": "Technical-preview local graph memory for DSH and Codex: recalls owner-global and current-project evidence before each turn, writes USER and ASSISTANT records separately, and preserves project, session, actor, and source provenance for a visual atlas and cited knowledge pages.", + "zh": "面向 DSH 与 Codex 的技术预览版本机图记忆:每轮前从用户全局与当前项目作用域召回证据,分别保存 USER 与 ASSISTANT 记录,并保留项目、会话、角色和来源信息,用于可视化图谱与带证据引用的知识页。" + }, + "npm": null, + "stars": 7, + "downloads": null, + "install": "dsh plugin --profile web add github:reshuibuduo/TMCRA-Agent-Memory#path:/integrations/deepseek-harness-local", + "added": "2026-08-16" + }, + { + "name": "dsh-openviking", + "owner": "Rxiain", + "url": "https://github.com/Rxiain/dsh-openviking", + "page": "https://awesome-dsh-plugin.com/p/Rxiain/dsh-openviking/", + "category": "memory", + "description": { + "en": "Semantic retrieval, resource management, auto-recall (user + agent dual spaces) and session memory for DeepSeek Harness: memsearch/memfind/memread/membrowse/memgrep/memglob over viking:// URIs, memadd/memremove resource management, memqueue observer status, memcommit session capture, and memlearn skill playbook minting.", + "zh": "面向 DeepSeek Harness 的 OpenViking 语义检索、资源管理、自动召回(user + agent 双空间)与会话记忆插件:基于 viking:// URI 的 memsearch/memfind/memread/membrowse/memgrep/memglob 检索工具、memadd/memremove 资源管理、memqueue 队列状态、memcommit 会话捕获,以及 memlearn 技能手册沉淀。" + }, + "npm": null, + "stars": 9, + "downloads": null, + "install": "dsh plugin --profile web add github:Rxiain/dsh-openviking", + "added": "2026-08-17" + }, + { + "name": "dsh-cognition", + "owner": "scd13150", + "url": "https://github.com/scd13150/dsh-cognition", + "page": "https://awesome-dsh-plugin.com/p/scd13150/dsh-cognition/", + "category": "memory", + "description": { + "en": "Project memory for coding agents on DeepSeek Harness: similar past edits surface as precedents, out-of-scope edits are blocked, and cognition persists across sessions (47 upstream-verified fixes, gold-free 20-task run).", + "zh": "给 DSH agent 的项目记忆:历史相似编辑作为先例出现、越界修改被 scope 锁拦下、认知跨会话持续积累(47 次上游验收修复、去 gold 化 20/20)。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:scd13150/dsh-cognition", + "added": "2026-08-15" + }, + { + "name": "dsh-memory", + "owner": "seriousz158", + "url": "https://github.com/seriousz158/dsh-memory", + "page": "https://awesome-dsh-plugin.com/p/seriousz158/dsh-memory/", + "category": "memory", + "description": { + "en": "Local Git-backed long-term memory for DeepSeek Harness with cited context, usage-aware read tools, safe sync, previews, and rollback.", + "zh": "基于本地 Git 的 DeepSeek Harness 长期记忆:带引用上下文、按使用反馈排序的读取工具、安全同步、预览与回滚。" + }, + "npm": "dsh-git-memory", + "stars": 94, + "downloads": 306, + "install": "dsh plugin --profile web add dsh-git-memory", + "added": "2026-08-22" + }, + { + "name": "dsh-memory-porter", + "owner": "Shiye-10Pages", + "url": "https://github.com/Shiye-10Pages/dsh-memory-porter", + "page": "https://awesome-dsh-plugin.com/p/Shiye-10Pages/dsh-memory-porter/", + "category": "memory", + "description": { + "en": "Brings your existing Claude / ChatGPT history into DSH: instant zero-token import of the memories.json inside a Claude export, local Claude Code transcripts with no export needed, and conversations distilled through the model you already configured in DSH. Every memory carries verbatim evidence checked against the source by code — a paraphrase is dropped and counted, and evidence found only in the assistant turns is always routed to review rather than stored.", + "zh": "把你在 Claude / ChatGPT 里已有的历史搬进 DSH:Claude 导出包里的 memories.json 零 token 瞬时入库,本机 Claude Code 记录免导出直读,历史对话用你在 DSH 里已配好的模型提纯。每条记忆都带由代码回原文核对的逐字证据——转述会被丢弃并计数,只在 AI 回复里找到的证据一律转人工确认而非直接入库。" + }, + "npm": "dsh-memory-porter", + "stars": 2, + "downloads": 389, + "install": "dsh plugin --profile web add dsh-memory-porter", + "added": "2026-08-18" + }, + { + "name": "dsh-engram", + "owner": "skepsun", + "url": "https://github.com/skepsun/dsh-engram", + "page": "https://awesome-dsh-plugin.com/p/skepsun/dsh-engram/", + "category": "memory", + "description": { + "en": "Zero-LLM auto-capture and a symbolic \\[ENGRAM\\] index with progressive disclosure, an ESR-lite task/evidence protocol (esr_task / esr_node / esr_close / esr_link), and per-workspace usage telemetry with an offline recall benchmark (npm run eval).", + "zh": "零 LLM 自动捕获 + 符号化 \\[ENGRAM\\] 索引渐进披露,配 ESR-lite 任务/证据闭环(esr_task / esr_node / esr_close / esr_link)、按工作区的真实使用遥测与离线召回基准(npm run eval)。" + }, + "npm": "dsh-engram", + "stars": 1, + "downloads": 523, + "install": "dsh plugin --profile web add dsh-engram", + "added": "2026-08-21" + }, + { + "name": "dsh-plugin-sodamem", + "owner": "SodaMem", + "url": "https://github.com/SodaMem/dsh-plugin-sodamem", + "page": "https://awesome-dsh-plugin.com/p/SodaMem/dsh-plugin-sodamem/", + "category": "memory", + "description": { + "en": "Bi-temporal memory backed by a SodaMem daemon: recall is contributed to the prompt while each turn is assembled and every closed turn is ingested, with no tool call and no model call on the read path; each recalled fact cites the turn it came from and carries validity dates, so superseded facts stop being retrieved.", + "zh": "基于 SodaMem 守护进程的双时间轴记忆:每轮组装提示词时注入检索结果,回合结束后自动写回,读路径不经工具调用也不额外调用模型;每条召回的事实都标注来源回合并带有效期,失效的事实不再被检索到。" + }, + "npm": "dsh-plugin-sodamem", + "stars": 2, + "downloads": 394, + "install": "dsh plugin --profile web add dsh-plugin-sodamem", + "added": "2026-08-18" + }, + { + "name": "dsh-auto-compact", + "owner": "songoao25", + "url": "https://github.com/songoao25/dsh-auto-compact", + "page": "https://awesome-dsh-plugin.com/p/songoao25/dsh-auto-compact/", + "category": "memory", + "description": { + "en": "Enhanced auto-compaction defaults for DeepSeek Harness agent presets: apply tuned threshold/retention budgets and per-model policies with one command, idempotent with backups and rollback.", + "zh": "为 DSH 智能体预设注入增强的自动压缩默认值:一键应用调优的触发阈值/保留比例与分模型策略,幂等执行、自动备份、可回滚。" + }, + "npm": null, + "stars": 3, + "downloads": null, + "install": "dsh plugin --profile web add github:songoao25/dsh-auto-compact", + "added": "2026-08-17" + }, + { + "name": "dsh-memos-bridge", + "owner": "Swd146296", + "url": "https://github.com/Swd146296/dsh-memos-bridge", + "page": "https://awesome-dsh-plugin.com/p/Swd146296/dsh-memos-bridge/", + "category": "memory", + "description": { + "en": "Bridges the MemOS memory service over MCP: the agent gets add/search/update/delete memory, multi-cube sharing, and a memory scheduler as mcp__memos__* tools.", + "zh": "通过 MCP 桥接 MemOS 记忆服务:agent 获得 mcp__memos__* 记忆写入/检索/更新/删除、多 Cube 共享与记忆调度工具。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:Swd146296/dsh-memos-bridge", + "added": "2026-08-22" + }, + { + "name": "dsh-akn-plugin", + "owner": "symmetryseeker", + "url": "https://github.com/symmetryseeker/dsh-akn-plugin", + "page": "https://awesome-dsh-plugin.com/p/symmetryseeker/dsh-akn-plugin/", + "category": "memory", + "description": { + "en": "Local-first Agent Experience Network for DeepSeek Harness, distilling task-level experiences from traces and live manifests, grading by H0-H4 evidence, discovering by compatibility, and promoting only after explicit review.", + "zh": "DeepSeek Harness 的本地优先 Agent 经验网络:从 trace 与实时 Manifest 蒸馏任务级经验,按 H0-H4 证据分级、按兼容性发现,经显式审阅后才晋升。" + }, + "npm": "dsh-akn-plugin", + "stars": 4, + "downloads": 430, + "install": "dsh plugin --profile web add dsh-akn-plugin", + "added": "2026-08-20" + }, + { + "name": "dsh-yogacara", + "owner": "tancheng33", + "url": "https://github.com/tancheng33/dsh-yogacara", + "page": "https://awesome-dsh-plugin.com/p/tancheng33/dsh-yogacara/", + "category": "memory", + "description": { + "en": "Tracks the agent's state as Yogācāra mental factors over a durable seed store, and feeds it back into the prompt as first-person inclination.", + "zh": "以唯识心所与种子藏记录 agent 的状态,并以第一人称倾向写回提示词。" + }, + "npm": "dsh-yogacara", + "stars": 4, + "downloads": 671, + "install": "dsh plugin --profile web add dsh-yogacara", + "added": "2026-08-17" + }, + { + "name": "flowix#dsh-flowix-memory", + "owner": "text2future", + "url": "https://github.com/text2future/flowix/tree/main/app/flowix-dsh-host/bundles/dsh-flowix-memory", + "page": "https://awesome-dsh-plugin.com/p/text2future/flowix--app-flowix-dsh-host-bundles-dsh-flowix-memory/", + "category": "memory", + "description": { + "en": "Registers the local flowix-cli MCP server so the agent can search, read, create, and edit Flowix memos and mind-map artifacts.", + "zh": "将本地 flowix-cli 注册为 MCP 服务,让 agent 可以搜索、读取、创建和编辑 Flowix 备忘与思维导图产物。" + }, + "npm": null, + "stars": 371, + "downloads": null, + "install": "dsh plugin --profile web add github:text2future/flowix#path:/app/flowix-dsh-host/bundles/dsh-flowix-memory", + "added": "2026-08-18", + "screenshots": [ + "https://raw.githubusercontent.com/text2future/flowix/main/docs/images/home-write.png", + "https://raw.githubusercontent.com/text2future/flowix/main/docs/images/readme-agent.png", + "https://raw.githubusercontent.com/text2future/flowix/main/docs/images/gh-detail-4.png", + "https://raw.githubusercontent.com/text2future/flowix/main/docs/images/gh-detail-6.png" + ] + }, + { + "name": "engramory#plugin", + "owner": "tinqiao-oss", + "url": "https://github.com/tinqiao-oss/engramory/tree/master/adapters/dsh/plugin", + "page": "https://awesome-dsh-plugin.com/p/tinqiao-oss/engramory--adapters-dsh-plugin/", + "category": "memory", + "description": { + "en": "Long-term memory for DeepSeek Harness kept as plain markdown, one fact per file, readable in any editor. The `MEMORY.md` index has a hard 200-line / 25 KB cap enforced by `ctx.tools.guard()`: a write that would grow an over-cap index is denied, while a rewrite that shrinks it always passes, so an oversized index can be compacted step by step. The plugin never writes memories on its own — what is kept is decided by you and the model. The same store is also used by Claude Code, Codex, Kiro and OpenClaw.", + "zh": "给 DeepSeek Harness 的长期记忆,以纯 markdown 保存:一条事实一个文件,任何编辑器都能打开。索引 `MEMORY.md` 有 200 行 / 25KB 的硬上限,由 `ctx.tools.guard()` 强制执行——让已超限的索引继续变大的写入会被拒绝,而缩小它的重写一律放行,于是过大的索引可以一步步压缩下来。插件不会自行写入记忆,记什么由你和模型决定。同一个记忆库也被 Claude Code、Codex、Kiro、OpenClaw 使用。" + }, + "npm": "dsh-engramory", + "stars": 174, + "downloads": 1875, + "install": "dsh plugin --profile web add dsh-engramory", + "added": "2026-08-16" + }, + { + "name": "dsh-plugin-recall", + "owner": "truelove-dreamer", + "url": "https://github.com/truelove-dreamer/dsh-plugin-recall", + "page": "https://awesome-dsh-plugin.com/p/truelove-dreamer/dsh-plugin-recall/", + "category": "memory", + "description": { + "en": "Cross-session memory for the model: full-text search all past sessions (SQLite FTS5 via ctx.sessionQuery) and bring the strongest matching excerpts back into the current context.", + "zh": "跨会话记忆:全文检索所有历史会话(复用 ctx.sessionQuery 的 SQLite FTS5 索引),把最强匹配片段带回当前上下文。" + }, + "npm": "dsh-plugin-recall", + "stars": 1, + "downloads": 282, + "install": "dsh plugin --profile web add dsh-plugin-recall", + "added": "2026-08-14" + }, + { + "name": "mosaic-memory-compress", + "owner": "TuringCorp-net", + "url": "https://github.com/TuringCorp-net/mosaic-memory-compress", + "page": "https://awesome-dsh-plugin.com/p/TuringCorp-net/mosaic-memory-compress/", + "category": "memory", + "description": { + "en": "Forgetting-curve in-place dialogue compression for DeepSeek Harness: recent turns stay verbatim, middle turns are structurally dehydrated (zero-LLM), ancient turns fold into a bounded summary pair — unbounded conversations on a bounded memory budget.", + "zh": "为 DeepSeek Harness 提供基于自然遗忘曲线的就地对话压缩:近期对话逐字保留、中期结构化脱水(零 LLM 成本)、久远对话折叠为有界摘要对——有限记忆预算上的无限对话。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:TuringCorp-net/mosaic-memory-compress", + "added": "2026-08-27" + }, + { + "name": "billion-context-dsh", + "owner": "Tyan66666", + "url": "https://github.com/Tyan66666/billion-context-dsh", + "page": "https://awesome-dsh-plugin.com/p/Tyan66666/billion-context-dsh/", + "category": "memory", + "description": { + "en": "Model-driven context compression (Active Context Pruning) for DeepSeek Harness: the model decides when and what to compress.", + "zh": "模型驱动的上下文压缩(Active Context Pruning):由模型决定何时压缩、压缩什么。" + }, + "npm": null, + "stars": 46, + "downloads": null, + "install": "dsh plugin --profile web add github:Tyan66666/billion-context-dsh", + "added": "2026-08-15" + }, + { + "name": "hindsight#coding-agents", + "owner": "vectorize-io", + "url": "https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/coding-agents", + "page": "https://awesome-dsh-plugin.com/p/vectorize-io/hindsight--hindsight-integrations-coding-agents/", + "category": "memory", + "description": { + "en": "Hindsight, agent memory that learns: long-term project memory with auto recall and retain, knowledge pages, deep reflection, and per-repo memory banks.", + "zh": "Hindsight:会学习的 Agent 长期记忆系统,自动召回/保存、知识页、深度反思与按仓库隔离的记忆银行。" + }, + "npm": "@vectorize-io/hindsight-coding-agents", + "stars": 21133, + "downloads": 25584, + "install": "dsh plugin --profile web add @vectorize-io/hindsight-coding-agents", + "added": "2026-08-15" + }, + { + "name": "DSH-LLM-wiki-plugin#dsh-llm-wiki", + "owner": "Vesna-Strivozha", + "url": "https://github.com/Vesna-Strivozha/DSH-LLM-wiki-plugin/tree/main/packages/dsh-llm-wiki", + "page": "https://awesome-dsh-plugin.com/p/Vesna-Strivozha/DSH-LLM-wiki-plugin--packages-dsh-llm-wiki/", + "category": "memory", + "description": { + "en": "An Obsidian-style knowledge base: ingest sources, let the LLM build and maintain an interlinked wiki, and ask questions grounded in it.", + "zh": "一个 Obsidian 风格的知识库:摄取资料,让 LLM 构建并维护互联 wiki,并基于它提问。" + }, + "npm": "@vesna-strivozha-2026/dsh-llm-wiki", + "stars": 3, + "downloads": 847, + "install": "dsh plugin --profile web add @vesna-strivozha-2026/dsh-llm-wiki", + "added": "2026-08-24" + }, + { + "name": "OpenViking#examples/dsh-memory-plugin", + "owner": "volcengine", + "url": "https://github.com/volcengine/OpenViking/tree/main/examples/dsh-memory-plugin", + "page": "https://awesome-dsh-plugin.com/p/volcengine/OpenViking--examples-dsh-memory-plugin/", + "category": "memory", + "description": { + "en": "OpenViking memory and context bundle for DeepSeek Harness: pre-step auto-recall and profile injection, session capture, `viking://` URI guarding, and recall/write memory tools backed by an OpenViking server.", + "zh": "面向 DeepSeek Harness 的 OpenViking 记忆与上下文插件:pre-step 自动召回与画像注入、会话捕获、`viking://` URI 防护,以及对接 OpenViking 服务端的 recall/write 记忆工具。" + }, + "npm": "@openviking/dsh-memory-plugin", + "stars": 33335, + "downloads": 5792, + "install": "dsh plugin --profile web add @openviking/dsh-memory-plugin", + "added": "2026-08-16" + }, + { + "name": "deja-vu#extensions/dsh", + "owner": "vshulcz", + "url": "https://github.com/vshulcz/deja-vu/tree/main/extensions/dsh", + "page": "https://awesome-dsh-plugin.com/p/vshulcz/deja-vu--extensions-dsh/", + "category": "memory", + "description": { + "en": "Reads the session files nineteen other coding agents on this machine already wrote — Claude Code, Codex, Cursor, opencode, Antigravity, Kimi, Cline, Zed and more — including sessions from before it was installed: six tools (deja_recall, deja_session, deja_blame, deja_fix, deja_how, deja_remember), a /deja command, and optional automatic recall added to the runtime context. Local BM25 index, no LLM, no embeddings, no network (dsh plugin --profile web add dsh-deja).", + "zh": "读取本机上其他十九个编程智能体已经写下的会话文件——Claude Code、Codex、Cursor、opencode、Antigravity、Kimi、Cline、Zed 等,包括安装之前的历史:提供 deja_recall、deja_session、deja_blame、deja_fix、deja_how、deja_remember 六个工具与 /deja 命令,并可选地把召回结果加入运行时上下文。本地 BM25 索引,不用大模型,不用向量嵌入,不联网(dsh plugin --profile web add dsh-deja)。" + }, + "npm": "dsh-deja", + "stars": 724, + "downloads": 1331, + "install": "dsh plugin --profile web add dsh-deja", + "added": "2026-08-23" + }, + { + "name": "dsh-md-notes", + "owner": "XieZongChen", + "url": "https://github.com/XieZongChen/dsh-md-notes", + "page": "https://awesome-dsh-plugin.com/p/XieZongChen/dsh-md-notes/", + "category": "memory", + "description": { + "en": "A markdown notes manager for DSH: full **MD notes manager** and **MD notes editor**, quickly capture conversations into notes, maintain notes by **syncing to Git repositories**, and **bring notes into the conversation context**.", + "zh": "DSH 的 MD 笔记管理插件:提供完整的 **MD 笔记管理器** 和 **MD 笔记编辑器**,对话内容可快速记入笔记。笔记可 **同步 Git 仓库** 维护。对话时可将笔记 **加入上下文**。" + }, + "npm": "dsh-md-notes", + "stars": 15, + "downloads": 1974, + "install": "dsh plugin --profile web add dsh-md-notes", + "added": "2026-08-17" + }, + { + "name": "dsh-plugin-asmemory", + "owner": "Xplore-LAB", + "url": "https://github.com/Xplore-LAB/dsh-plugin-asmemory", + "page": "https://awesome-dsh-plugin.com/p/Xplore-LAB/dsh-plugin-asmemory/", + "category": "memory", + "description": { + "en": "Action-state time memory: record typed states and actions, then analyze trends, anomalies, and causality.", + "zh": "动作-状态时序记忆:记录类型化的状态与动作,做趋势、异常与因果关联分析。" + }, + "npm": null, + "stars": 0, + "downloads": null, + "install": "dsh plugin --profile web add github:Xplore-LAB/dsh-plugin-asmemory", + "added": "2026-08-14" + }, + { + "name": "dsh-memory", + "owner": "yangyongzhen", + "url": "https://github.com/yangyongzhen/dsh-memory", + "page": "https://awesome-dsh-plugin.com/p/yangyongzhen/dsh-memory/", + "category": "memory", + "description": { + "en": "Long-term memory injected at session start: preferences/facts/summaries/knowledge in global and per-project scopes, budgeted recall via `agent/pre-step`, durable JSON store.", + "zh": "会话开始自动注入的长期记忆:preference/fact/summary/knowledge 四类、global/project 双作用域,`agent/pre-step` 字节预算召回,JSON 原子持久化。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:yangyongzhen/dsh-memory", + "added": "2026-08-15" + }, + { + "name": "dsh-cc-haha-memory", + "owner": "yihefeikong-rgb", + "url": "https://github.com/yihefeikong-rgb/dsh-cc-haha-memory", + "page": "https://awesome-dsh-plugin.com/p/yihefeikong-rgb/dsh-cc-haha-memory/", + "category": "memory", + "description": { + "en": "Persistent memory for DeepSeek Harness: memory read/write/retrieve tools, per-turn auto-recording, project-scoped recall injection and a management UI, with non-destructive Claude Code memory import.", + "zh": "DeepSeek Harness 持久记忆插件:记忆读写/检索工具、回合自动记录、项目作用域召回注入、管理界面,可非破坏导入 Claude Code 记忆。" + }, + "npm": null, + "tarball": "https://github.com/yihefeikong-rgb/dsh-cc-haha-memory/releases/latest/download/dsh-memory-0.1.0.tgz", + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add \"https://github.com/yihefeikong-rgb/dsh-cc-haha-memory/releases/latest/download/dsh-memory-0.1.0.tgz\"", + "added": "2026-08-21" + }, + { + "name": "dsh-agentmemory", + "owner": "Yiipu", + "url": "https://github.com/Yiipu/dsh-agentmemory", + "page": "https://awesome-dsh-plugin.com/p/Yiipu/dsh-agentmemory/", + "category": "memory", + "description": { + "en": "DSH ↔ agentmemory session-memory bridge: mirrors session lifecycle into a local agentmemory daemon (REST), exposes memory_recall / memory_remember tools, and injects a per-session memory context window via agent/pre-step.", + "zh": "DSH ↔ agentmemory 会话记忆桥:把会话生命周期镜像到本地 agentmemory 守护进程(REST),提供 memory_recall / memory_remember 工具,并通过 agent/pre-step 按会话注入记忆上下文窗口。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Yiipu/dsh-agentmemory", + "added": "2026-08-15" + }, + { + "name": "qmd-autosearch", + "owner": "zhangzhenwen1", + "url": "https://github.com/zhangzhenwen1/qmd-autosearch", + "page": "https://awesome-dsh-plugin.com/p/zhangzhenwen1/qmd-autosearch/", + "category": "memory", + "description": { + "en": "Auto-supplement QMD semantic search when the model greps/globs a knowledge-base directory: zero-dependency DSH plugin, async injection via next-step, no manual invocation.", + "zh": "模型在知识库目录执行 grep/glob 搜索时自动补充 QMD 语义检索:零依赖 DSH 插件,异步注入下一步上下文,无需手动调用。" + }, + "npm": "qmd-autosearch", + "stars": 1, + "downloads": 385, + "install": "dsh plugin --profile web add qmd-autosearch", + "added": "2026-08-16" + }, + { + "name": "dsh-plugin-thread", + "owner": "zhaoyuntao-wl", + "url": "https://github.com/zhaoyuntao-wl/dsh-plugin-thread", + "page": "https://awesome-dsh-plugin.com/p/zhaoyuntao-wl/dsh-plugin-thread/", + "category": "memory", + "description": { + "en": "Decision-and-goal memory with lineage: explicit commands and a model tool record decisions deterministically, a status card re-anchors them across compaction, a lossless event stream with BM25+jieba retrieval backs it, and a scenario-level fidelity regression suite gates releases.", + "zh": "带血缘的决策与目标记忆:显式命令与模型工具确定性记录决策,状态卡跨压缩重锚定,无损事件流 + BM25 分词检索兜底,场景级保真回归集作发布门禁。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:zhaoyuntao-wl/dsh-plugin-thread", + "added": "2026-08-24" + }, + { + "name": "dsh-flomo", + "owner": "zhengjy01", + "url": "https://github.com/zhengjy01/dsh-flomo", + "page": "https://awesome-dsh-plugin.com/p/zhengjy01/dsh-flomo/", + "category": "memory", + "description": { + "en": "Write notes and memos to flomo (浮墨笔记) from any agent via the flomo_send tool, configured once with the flomo API URL or API Key.", + "zh": "配置一次 flomo API URL / API Key,Agent 即可用 flomo_send 把笔记、摘要、待办写进 flomo(浮墨笔记)。" + }, + "npm": "dsh-flomo", + "stars": 1, + "downloads": 389, + "install": "dsh plugin --profile web add dsh-flomo", + "added": "2026-08-15" + }, + { + "name": "dsh-notion-connector", + "owner": "zhengjy01", + "url": "https://github.com/zhengjy01/dsh-notion-connector", + "page": "https://awesome-dsh-plugin.com/p/zhengjy01/dsh-notion-connector/", + "category": "memory", + "description": { + "en": "Notion connector for DeepSeek Harness: search, read, query, create, update, and append Notion pages and databases via six agent tools, with a Web settings page — configured once with an Integration Token.", + "zh": "配置一次 Notion Integration Token,Agent 即可搜索、读取、查询、创建、更新、追加 Notion 页面与数据库内容,并提供 Web 设置页。" + }, + "npm": "dsh-notion-connector", + "stars": 1, + "downloads": 397, + "install": "dsh plugin --profile web add dsh-notion-connector", + "added": "2026-08-16" + }, + { + "name": "dsh-memory-system", + "owner": "zhujunpeng12", + "url": "https://github.com/zhujunpeng12/dsh-memory-system", + "page": "https://awesome-dsh-plugin.com/p/zhujunpeng12/dsh-memory-system/", + "category": "memory", + "description": { + "en": "Local-first persistent memory for DeepSeek Harness: bounded pre-step hot-memory injection, exact and Chinese-bigram BM25 cold recall, six agent tools, approval-gated lease-lock writes with recovery receipts, and read-only governance and trajectory-review candidates.", + "zh": "面向 DeepSeek Harness 的本地优先持久记忆:在 pre-step 注入有预算上限的热记忆,使用精确匹配与中文二元 BM25 召回冷层,提供六个 Agent 工具,并以审批门禁、租约锁、恢复回执和只读治理与轨迹复盘候选保护写入。" + }, + "npm": "@zhujunpeng12/dsh-memory-system", + "tarball": "https://github.com/zhujunpeng12/dsh-memory-system/releases/download/v0.1.1/zhujunpeng12-dsh-memory-system-0.1.1.tgz", + "stars": 8, + "downloads": 174, + "install": "dsh plugin --profile web add @zhujunpeng12/dsh-memory-system", + "added": "2026-08-21", + "screenshots": [ + "https://raw.githubusercontent.com/zhujunpeng12/dsh-memory-system/master/assets/social-preview.png", + "https://raw.githubusercontent.com/zhujunpeng12/dsh-memory-system/master/docs/memory-system-flowchart.png" + ] + }, + { + "name": "memsearch#MemSearch", + "owner": "zilliztech", + "url": "https://github.com/zilliztech/memsearch/tree/main/plugins/dsh", + "page": "https://awesome-dsh-plugin.com/p/zilliztech/memsearch--plugins-dsh/", + "category": "memory", + "description": { + "en": "Shared Markdown memory for DSH and other coding agents, with automatic capture, pre-step context injection, searchable recall, and memory-to-skill self-evolution through a review panel.", + "zh": "供 DSH 与其他编程 Agent 共享的 Markdown 记忆,支持自动捕获、步骤前上下文注入、搜索召回,以及通过审阅面板实现 memory-to-skill 自进化。" + }, + "npm": null, + "stars": 2512, + "downloads": null, + "install": "dsh plugin --profile web add github:zilliztech/memsearch#path:/plugins/dsh", + "added": "2026-08-24" + }, + { + "name": "dsh-noema", + "owner": "ZSeven-W", + "url": "https://github.com/ZSeven-W/dsh-noema", + "page": "https://awesome-dsh-plugin.com/p/ZSeven-W/dsh-noema/", + "category": "memory", + "description": { + "en": "Long-term memory for DSH agents — durable, inspectable memories with recall, search, browse and knowledge-graph tools, import from ten other AI coding tools, and a settings page.", + "zh": "为 DSH Agent 提供长期记忆——可检索、可检视的持久记忆,带回忆、搜索、浏览与知识图谱工具,支持从十个其他 AI 编码工具导入记忆,并附设置页。" + }, + "npm": "@zseven-w/dsh-noema", + "stars": 126, + "downloads": 1324, + "install": "dsh plugin --profile web add @zseven-w/dsh-noema", + "added": "2026-08-19" + }, + { + "name": "dsh-crypto-portfolio", + "owner": "0xRabit", + "url": "https://github.com/0xRabit/dsh-crypto-portfolio", + "page": "https://awesome-dsh-plugin.com/p/0xRabit/dsh-crypto-portfolio/", + "category": "tools", + "description": { + "en": "Crypto portfolio tracker for DSH: BTC / EVM (DeBank free+paid) / Solana / Hyperliquid L1 / CEX balances with multi-provider API failover, per-profile configs, scheduled daily refresh and trend charts (self-contained web dashboard).", + "zh": "加密货币组合追踪器:BTC / EVM(DeBank 免费+付费双源)/ Solana / Hyperliquid L1 / CEX 余额,多 API 源自动切换、多 Profile 配置、每日定时刷新与趋势图(自带 Web 仪表盘)。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:0xRabit/dsh-crypto-portfolio", + "added": "2026-08-23" + }, + { + "name": "dsh-wsl-net", + "owner": "173787247", + "url": "https://github.com/173787247/dsh-wsl-net", + "page": "https://awesome-dsh-plugin.com/p/173787247/dsh-wsl-net/", + "category": "tools", + "description": { + "en": "Adds a net_doctor tool that reports proxy environment, NODE_USE_ENV_PROXY, and reachability of the DeepSeek API and the npm registry, and sets NODE_USE_ENV_PROXY on bash and npm child processes.", + "zh": "提供 net_doctor 工具,报告代理环境、NODE_USE_ENV_PROXY,以及 DeepSeek API 与 npm registry 的连通性,并为 bash 和 npm 子进程设置 NODE_USE_ENV_PROXY。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:173787247/dsh-wsl-net", + "added": "2026-08-26" + }, + { + "name": "dsh-hdc-bridge", + "owner": "1na-ko", + "url": "https://github.com/1na-ko/dsh-hdc-bridge", + "page": "https://awesome-dsh-plugin.com/p/1na-ko/dsh-hdc-bridge/", + "category": "tools", + "description": { + "en": "HarmonyOS device bridge: hdc screenshot/install/log/crash/UI automation loop with read_image, official-first versioned API knowledge (SDK .d.ts + offline bundled docs), and a DevEco CLI build/sign/lint lane.", + "zh": "鸿蒙设备桥:hdc 截图/装包/日志/崩溃/UI 自动化闭环(配 read_image 看图),官方优先版本化 API 知识层(SDK .d.ts + 离线随包文档),以及 DevEco CLI 构建/签名/lint 通道。" + }, + "npm": "dsh-hdc-bridge", + "stars": 16, + "downloads": 1414, + "install": "dsh plugin --profile web add dsh-hdc-bridge", + "added": "2026-08-14" + }, + { + "name": "dsh-wsl-workspace", + "owner": "6Mikao9", + "url": "https://github.com/6Mikao9/dsh-wsl-workspace", + "page": "https://awesome-dsh-plugin.com/p/6Mikao9/dsh-wsl-workspace/", + "category": "tools", + "description": { + "en": "Add a WSL workspace from the web GUI without needing to install dsh or related tools again inside WSL. Bash commands and file read/write operations run within the local WSL distribution on the host machine, while Windows files remain accessible.", + "zh": "从 Web GUI 添加 WSL 工作区,无需在 WSL 之中再次安装 dsh 以及相关工具,bash 命令与文件读写运行在本机 WSL 发行版内,Windows 文件仍可访问。" + }, + "npm": "dsh-wsl-workspace", + "stars": 36, + "downloads": 1132, + "install": "dsh plugin --profile web add dsh-wsl-workspace", + "added": "2026-08-14" + }, + { + "name": "dsh-plugin-academic-writing", + "owner": "863683348", + "url": "https://github.com/863683348/dsh-plugin-academic-writing", + "page": "https://awesome-dsh-plugin.com/p/863683348/dsh-plugin-academic-writing/", + "category": "tools", + "description": { + "en": "Academic writing toolkit for DSH agents: paper outlines, title and abstract skeletons, GB/T 7714 / APA / MLA citations, phrasing QA, and a pre-submission checklist.", + "zh": "为 DSH agent 提供学术写作工具包:论文大纲、标题与摘要骨架、GB/T 7714 / APA / MLA 引文格式化、措辞质检与投稿前清单。" + }, + "npm": "dsh-plugin-academic-writing", + "stars": 1, + "downloads": 683, + "install": "dsh plugin --profile web add dsh-plugin-academic-writing", + "added": "2026-08-17" + }, + { + "name": "dsh-plugin-education", + "owner": "863683348", + "url": "https://github.com/863683348/dsh-plugin-education", + "page": "https://awesome-dsh-plugin.com/p/863683348/dsh-plugin-education/", + "category": "tools", + "description": { + "en": "Education toolkit for DSH agents: lesson-plan skeletons by grade band, quiz scaffolding and answer-key validation, analytic rubrics, Anki/markdown flashcards, and readability levels.", + "zh": "为 DSH agent 提供教育工具包:按学段的教案骨架、测验题与答案校验、分析式评分量规、Anki/markdown 闪卡与可读性分级。" + }, + "npm": "dsh-plugin-education", + "stars": 1, + "downloads": 415, + "install": "dsh plugin --profile web add dsh-plugin-education", + "added": "2026-08-17" + }, + { + "name": "dsh-plugin-finance-data", + "owner": "863683348", + "url": "https://github.com/863683348/dsh-plugin-finance-data", + "page": "https://awesome-dsh-plugin.com/p/863683348/dsh-plugin-finance-data/", + "category": "tools", + "description": { + "en": "Finance data toolkit for DSH agents: number and currency formatting (incl. Chinese wan/yi units), return and CAGR math, valuation and profitability ratios, PV/FV, risk metrics, and a data-quality checklist.", + "zh": "为 DSH agent 提供金融数据工具包:数字与货币格式化(含中文万/亿单位)、收益与年化计算、估值与盈利比率、现值/终值、风险指标与数据质量清单。" + }, + "npm": "dsh-plugin-finance-data", + "stars": 6, + "downloads": 492, + "install": "dsh plugin --profile web add dsh-plugin-finance-data", + "added": "2026-08-17" + }, + { + "name": "dsh-plugin-local-life", + "owner": "863683348", + "url": "https://github.com/863683348/dsh-plugin-local-life", + "page": "https://awesome-dsh-plugin.com/p/863683348/dsh-plugin-local-life/", + "category": "tools", + "description": { + "en": "Local-life toolkit for DSH agents: budget planning, bill splitting, unit-price and discount math, common unit conversions (jin/li/chi, C/F, L/gallon), and trip checklists.", + "zh": "为 DSH agent 提供本地生活工具包:预算规划、账单分摊、单价与折扣计算、常用单位换算(斤/里/尺、摄氏/华氏、升/加仑)与出行清单。" + }, + "npm": "dsh-plugin-local-life", + "stars": 1, + "downloads": 380, + "install": "dsh plugin --profile web add dsh-plugin-local-life", + "added": "2026-08-17" + }, + { + "name": "dsh-plugin-translation", + "owner": "863683348", + "url": "https://github.com/863683348/dsh-plugin-translation", + "page": "https://awesome-dsh-plugin.com/p/863683348/dsh-plugin-translation/", + "category": "tools", + "description": { + "en": "Translation toolkit for DSH agents: sentence segmentation, term and glossary extraction, source-target QA (numbers, units, brackets), tone guides, and a durable workspace translation memory.", + "zh": "为 DSH agent 提供翻译工具包:句子分块、术语与术语表提取、译文质检(数字、单位、括号)、语气指南与工作区持久化翻译记忆。" + }, + "npm": "dsh-plugin-translation", + "stars": 2, + "downloads": 416, + "install": "dsh plugin --profile web add dsh-plugin-translation", + "added": "2026-08-17" + }, + { + "name": "dsh-computer-use", + "owner": "988hj7tczd-oss", + "url": "https://github.com/988hj7tczd-oss/dsh-computer-use", + "page": "https://awesome-dsh-plugin.com/p/988hj7tczd-oss/dsh-computer-use/", + "category": "tools", + "description": { + "en": "Cross-platform Computer Use for DeepSeek Harness: virtual-mouse human-like operation (screen_observe + computer_click + type + key + scroll + drag, 11 tools), AX-tree zero-vision-cost mode with free GLM-4V-Flash vision fallback, safety guards (snapshot TTL / app whitelist / dangerous-action approval / password-box protection).", + "zh": "跨平台 Computer Use 插件:虚拟鼠标真人操作(screen_observe / computer_click / type / key / scroll / drag 共 11 个工具),AX 语义树零视觉成本 + GLM-4V-Flash 免费视觉兜底,安全护栏(快照 TTL / 应用白名单 / 危险操作审批 / 密码框保护)。" + }, + "npm": null, + "stars": 15, + "downloads": null, + "install": "dsh plugin --profile web add github:988hj7tczd-oss/dsh-computer-use", + "added": "2026-08-16" + }, + { + "name": "dsh-toolbox-web", + "owner": "AbcdefgXW", + "url": "https://github.com/AbcdefgXW/dsh-toolbox-web", + "page": "https://awesome-dsh-plugin.com/p/AbcdefgXW/dsh-toolbox-web/", + "category": "tools", + "description": { + "en": "Toolbox plugin for the Web UI: session/trash/subdirectory/search/preset/config management, tag grouping, long-message collapse, and a scheduled heartbeat with optional IM channel push.", + "zh": "Web 界面工具箱:会话/回收站/子目录/搜索/预设/配置管理、标签分组、长消息折叠,以及支持 IM 渠道推送的定时心跳。" + }, + "npm": "dsh-toolbox-web", + "stars": 5, + "downloads": 2554, + "install": "dsh plugin --profile web add dsh-toolbox-web", + "added": "2026-08-16" + }, + { + "name": "dsh-monitor", + "owner": "AbnerAI", + "url": "https://github.com/AbnerAI/dsh-monitor", + "page": "https://awesome-dsh-plugin.com/p/AbnerAI/dsh-monitor/", + "category": "tools", + "description": { + "en": "Persistent background watchers that wake the agent on new messages (file inbox or command-output delta) — the harness analog of Claude Code's Monitor tool.", + "zh": "常驻后台监视器:文件收件箱(NDJSON)或命令输出增量一到就唤醒 agent,相当于 Claude Code Monitor 工具的 Harness 实现。" + }, + "npm": "dsh-monitor", + "stars": 3, + "downloads": 295, + "install": "dsh plugin --profile web add dsh-monitor", + "added": "2026-08-15" + }, + { + "name": "dsh-quick-launch", + "owner": "acebang0303", + "url": "https://github.com/acebang0303/dsh-quick-launch", + "page": "https://awesome-dsh-plugin.com/p/acebang0303/dsh-quick-launch/", + "category": "tools", + "description": { + "en": "One-click create, update or remove the DeepSeek Harness desktop shortcut with switchable custom icons and a draggable whale button.", + "zh": "一键创建、更新或删除 DeepSeek Harness 桌面快捷方式,图标可自定义上传切换,蓝白金鲸鱼悬浮按钮可拖动,点击旋转 90° 弹出面板。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:acebang0303/dsh-quick-launch", + "added": "2026-08-19", + "screenshots": [ + "https://raw.githubusercontent.com/acebang0303/dsh-quick-launch/main/docs/preview.png" + ] + }, + { + "name": "dsh-agentmail", + "owner": "agentmail-to", + "url": "https://github.com/agentmail-to/dsh-agentmail", + "page": "https://awesome-dsh-plugin.com/p/agentmail-to/dsh-agentmail/", + "category": "tools", + "description": { + "en": "Gives an agent its own email inbox through AgentMail — eleven tools for sending, reading and labelling mail, plus inbound mail delivered into one harness session per email thread, with an approval gate and recipient allowlist on everything outbound.", + "zh": "用 AgentMail 给 agent 配一个自己的收件箱:发信、读信、搜索与打标签共十一个工具;收到的邮件按邮件线程分派,每个线程对应一个独立会话;所有外发邮件都要过审批与收件人白名单。" + }, + "npm": "dsh-agentmail", + "stars": 2, + "downloads": 192, + "install": "dsh plugin --profile web add dsh-agentmail", + "added": "2026-08-18" + }, + { + "name": "dsh-skill-mcp-manager", + "owner": "alone-tree", + "url": "https://github.com/alone-tree/dsh-skill-mcp-manager", + "page": "https://awesome-dsh-plugin.com/p/alone-tree/dsh-skill-mcp-manager/", + "category": "tools", + "description": { + "en": "One-stop visual management of DSH Skills & MCP: on-demand MCP loading, in-session hot reload, SKILL & MCP descriptions viewable in the plugin.", + "zh": "一站式可视化管理 DSH 的 SKILL 与 MCP:MCP按需加载、会话内热重载、SKILL与MCP描述可在插件内直接查看" + }, + "npm": "dsh-skill-mcp-manager", + "stars": 1, + "downloads": 113, + "install": "dsh plugin --profile web add dsh-skill-mcp-manager", + "added": "2026-08-21", + "screenshots": [ + "https://raw.githubusercontent.com/alone-tree/dsh-skill-mcp-manager/main/docs/screenshot-mcp.png", + "https://raw.githubusercontent.com/alone-tree/dsh-skill-mcp-manager/main/docs/screenshot-skills.png" + ] + }, + { + "name": "dsh-multi-folder", + "owner": "AngelosZou", + "url": "https://github.com/AngelosZou/dsh-multi-folder", + "page": "https://awesome-dsh-plugin.com/p/AngelosZou/dsh-multi-folder/", + "category": "tools", + "description": { + "en": "Secondary working directories for a DSH project: the agent keeps the primary workspace as cwd and gains equal read/write/exec permissions on configured secondary directories, configurable from the session header and the new-session page.", + "zh": "为 DSH 项目提供副工作目录:agent 保持主工作区为 cwd,同时对已配置的副目录获得同等读写/执行权限,可在会话头部与新会话页配置。" + }, + "npm": "dsh-multi-folder", + "stars": 8, + "downloads": 998, + "install": "dsh plugin --profile web add dsh-multi-folder", + "added": "2026-08-15" + }, + { + "name": "dsh-plugin-sync", + "owner": "AngLi1997", + "url": "https://github.com/AngLi1997/dsh-plugin-sync", + "page": "https://awesome-dsh-plugin.com/p/AngLi1997/dsh-plugin-sync/", + "category": "tools", + "description": { + "en": "Sync the installed DeepSeek Harness plugin manifest to a GitHub Gist (OAuth login, one-click export/import with automatic dependency install).", + "zh": "将已安装的 DeepSeek Harness 插件清单同步到 GitHub Gist(OAuth 登录、一键导出/导入并自动安装依赖)。" + }, + "npm": "dsh-plugin-sync", + "stars": 3, + "downloads": 249, + "install": "dsh plugin --profile web add dsh-plugin-sync", + "added": "2026-08-15" + }, + { + "name": "dsh-computer-use", + "owner": "Anionex", + "url": "https://github.com/Anionex/dsh-computer-use", + "page": "https://awesome-dsh-plugin.com/p/Anionex/dsh-computer-use/", + "category": "tools", + "description": { + "en": "Accessibility-first macOS computer use: fresh observations, stale-state rejection, scoped permissions, and safe input.", + "zh": "macOS 电脑控制:Accessibility 观测、过期状态拒绝、作用域权限与安全输入。" + }, + "npm": "@anionex/dsh-computer-use", + "stars": 31, + "downloads": 788, + "install": "dsh plugin --profile web add @anionex/dsh-computer-use", + "added": "2026-08-13" + }, + { + "name": "dsh-cos", + "owner": "annexwu", + "url": "https://github.com/annexwu/dsh-cos", + "page": "https://awesome-dsh-plugin.com/p/annexwu/dsh-cos/", + "category": "tools", + "description": { + "en": "Adds COS storage browsing and transfers, workspace artifact uploads, temporary links, and conversation attachments that retain local copies with cloud object identity, plus Agent tools for Tencent Cloud COS, CI, and MetaInsight.", + "zh": "提供 COS 云存储浏览与传输、工作区产物上传、临时链接,以及同时保留本地副本和云端对象身份的会话附件,并提供腾讯云 COS、数据万象和 MetaInsight 的 Agent 工具。" + }, + "npm": "dsh-cos", + "stars": 2, + "downloads": 1650, + "install": "dsh plugin --profile web add dsh-cos", + "added": "2026-08-20" + }, + { + "name": "anysearch-dsh", + "owner": "anysearch-team", + "url": "https://github.com/anysearch-team/anysearch-dsh", + "page": "https://awesome-dsh-plugin.com/p/anysearch-team/anysearch-dsh/", + "category": "tools", + "description": { + "en": "AnySearch-powered real-time web and vertical search provider for DeepSeek Harness.", + "zh": "基于 AnySearch 的实时网页与垂直搜索插件,为 DeepSeek Harness 提供搜索工具。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:anysearch-team/anysearch-dsh", + "added": "2026-08-26" + }, + { + "name": "dsh-logtimeline", + "owner": "anyuer678", + "url": "https://github.com/anyuer678/dsh-logtimeline", + "page": "https://awesome-dsh-plugin.com/p/anyuer678/dsh-logtimeline/", + "category": "tools", + "description": { + "en": "Query local log files with Chinese natural-language time expressions (e.g. \"yesterday afternoon\", \"3 hours ago\") — precise time range, matched lines, ERROR/WARN/INFO level stats, fully offline.", + "zh": "用中文自然语言时间描述查询本地日志(如「昨天下午」「3小时前」)——精确时间范围、匹配行、级别统计,完全离线。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:anyuer678/dsh-logtimeline", + "added": "2026-08-18" + }, + { + "name": "dsh-expert-mode", + "owner": "Asher-2000", + "url": "https://github.com/Asher-2000/dsh-expert-mode", + "page": "https://awesome-dsh-plugin.com/p/Asher-2000/dsh-expert-mode/", + "category": "tools", + "description": { + "en": "Expert-mode agent preset for DeepSeek Harness (v0.9.2, npm dsh-expert-mode, bilingual EN/ZH): a chief coordinator plus 17 domain-expert subagents with automatic task delegation. Features: taskboard scheduler (file-system task state machine pending/ready/running/done/failed, dependency DAG, atomic claim, retry, crash recovery), quality gates (5-stage pipeline for high-risk tasks: requirement clarity, implementation, verification, independent review, integration, with 2-round rework limit), Five-Anchor constraint (review/convergence/anti-drift/collaboration-check/resource-awareness per turn), Near-distance Guidance (identity/task/output template per expert), progressive disclosure (~28% token savings), expert persistence, inter-expert file message bus (direct P2P comm, zero coordinator relay), cross review, experience pool, fast-track for simple tasks, fault recovery with auto-retry. Experts: data analyst, copywriter, legal review, product manager, frontend, UI/UX, architect, social media ops, growth hacker, quant finance, finance, backend, DevOps, database, QA, security.", + "zh": "专家模式 agent preset(v0.9.2,npm 包 dsh-expert-mode,双语双份):首席协调官 + 17 位领域专家子代理,按任务特性自动委派。特性:任务调度器(文件系统任务状态机 pending/ready/running/done/failed + 依赖DAG + 原子认领 + 重试 + 崩溃恢复)、质量门禁(高风险任务五段式:需求澄清→实现→验证→独立评审→集成,最多回炉2轮)、五锚约束(每轮回顾/收敛/反跑题/协作检查/资源感知自检)+ 近距离引导(身份/任务/输出格式模板)+ 渐进式披露(省token约28%)+ 专家持久化 + 专家间文件消息总线(P2P直连,零协调官中转)+ 交叉评审 + 经验沉淀 + 简单任务快速通道 + 故障自动重试。专家:数据分析/文案/法务/产品/前端/UI-UX/架构/社交运营/增长/量化/财务/后端/DevOps/数据库/QA/安全/生图短视频。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:Asher-2000/dsh-expert-mode", + "added": "2026-08-16" + }, + { + "name": "bailinghub-dsh-plugin", + "owner": "bailinghub", + "url": "https://github.com/bailinghub/bailinghub-dsh-plugin", + "page": "https://awesome-dsh-plugin.com/p/bailinghub/bailinghub-dsh-plugin/", + "category": "tools", + "description": { + "en": "Let a local DeepSeek Harness Agent use BailingHub to query data, update records, and run permitted actions in an online store, SaaS, or other admin system.", + "zh": "让本地 DeepSeek Harness 智能体通过 BailingHub,直接操作商城、SaaS 或其他业务系统后台,在现有账号权限内查询数据、修改资料并执行已开放的操作。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:bailinghub/bailinghub-dsh-plugin", + "added": "2026-08-28", + "screenshots": [ + "https://raw.githubusercontent.com/bailinghub/bailinghub-dsh-plugin/HEAD/assets/screenshots/01-local-agent-business-action.png", + "https://raw.githubusercontent.com/bailinghub/bailinghub-dsh-plugin/HEAD/assets/screenshots/02-connect-and-authorize.png", + "https://raw.githubusercontent.com/bailinghub/bailinghub-dsh-plugin/HEAD/assets/screenshots/03-existing-permissions-apply.png", + "https://raw.githubusercontent.com/bailinghub/bailinghub-dsh-plugin/HEAD/assets/screenshots/04-bailinghub-action-trail.png" + ] + }, + { + "name": "dsh-nuke-plugin", + "owner": "beijingwahw", + "url": "https://github.com/beijingwahw/dsh-nuke-plugin", + "page": "https://awesome-dsh-plugin.com/p/beijingwahw/dsh-nuke-plugin/", + "category": "tools", + "description": { + "en": "Transactional uninstall engine: every destructive action runs validate/preview/execute/undo with Saga rollback, WAL crash recovery, hash-chain audit, hardlink dedup, and a Bayesian oracle that predicts success probability before you commit.", + "zh": "事务化卸载引擎:每个破坏性动作走 validate/preview/execute/undo 四段式并带 Saga 回滚,WAL 崩溃恢复、hash chain 审计、硬链接去重,提交前先知推演成功率。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:beijingwahw/dsh-nuke-plugin", + "added": "2026-08-25" + }, + { + "name": "dsh-win-gitbash", + "owner": "buhuikongpan", + "url": "https://github.com/buhuikongpan/dsh-win-gitbash", + "page": "https://awesome-dsh-plugin.com/p/buhuikongpan/dsh-win-gitbash/", + "category": "tools", + "description": { + "en": "Git Bash shell tool for Windows: runs commands through the bash bundled with Git for Windows, with timeout, sandbox, output truncation, and background jobs, replacing the slow pwsh tool and the WSL-only bash tool.", + "zh": "面向 Windows 的 Git Bash 工具:通过 Git for Windows 自带的 bash 执行命令,支持超时、沙箱、输出截断与后台任务,替代卡顿的 pwsh 工具与仅限 WSL 的 bash 工具。" + }, + "npm": null, + "stars": 8, + "downloads": null, + "install": "dsh plugin --profile web add github:buhuikongpan/dsh-win-gitbash", + "added": "2026-08-15" + }, + { + "name": "dsh-session-audit", + "owner": "bwndlct", + "url": "https://github.com/bwndlct/dsh-session-audit", + "page": "https://awesome-dsh-plugin.com/p/bwndlct/dsh-session-audit/", + "category": "tools", + "description": { + "en": "Session execution analytics: steps, tool calls, failures, repeated actions, token usage and verification signals, rendered as text/Markdown/JSON reports.", + "zh": "会话执行分析:步骤、工具调用、失败、重复动作、token 用量与验证信号,输出 text/Markdown/JSON 报告。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:bwndlct/dsh-session-audit", + "added": "2026-08-14" + }, + { + "name": "dsh-omv", + "owner": "bx33661", + "url": "https://github.com/bx33661/dsh-omv", + "page": "https://awesome-dsh-plugin.com/p/bx33661/dsh-omv/", + "category": "tools", + "description": { + "en": "Evidence-first vulnerability research workbench for DeepSeek Harness: native audit views, 29 OMV tools, Evidence.v1 findings, reproducible runs, Campaign workflows, and isolated PoC evidence.", + "zh": "面向 DeepSeek Harness 的证据优先漏洞研究工作台:原生审计视图、29 个 OMV 工具、Evidence.v1 Finding、可复现 Run、Campaign 工作流和隔离 PoC 证据。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:bx33661/dsh-omv", + "added": "2026-08-18" + }, + { + "name": "dsh-ssh-ops", + "owner": "caoyiwei850", + "url": "https://github.com/caoyiwei850/dsh-ssh-ops", + "page": "https://awesome-dsh-plugin.com/p/caoyiwei850/dsh-ssh-ops/", + "category": "tools", + "description": { + "en": "SSH ops terminal for DSH Web UI: drive a connected server from the main conversation (ssh_connect/ssh_exec/ssh_read/ssh_write/ssh_disconnect) while keeping an interactive xterm.js terminal on the right.", + "zh": "DSH SSH 运维终端:在主对话中直接指挥已连接的服务器(ssh_connect/ssh_exec/ssh_read/ssh_write/ssh_disconnect),右侧保留可交互的 xterm.js 终端。" + }, + "npm": "dsh-ssh-ops", + "stars": 10, + "downloads": 2921, + "install": "dsh plugin --profile web add dsh-ssh-ops", + "added": "2026-08-15" + }, + { + "name": "dsh-asimovbox", + "owner": "cerebrixos-org", + "url": "https://github.com/cerebrixos-org/dsh-asimovbox", + "page": "https://awesome-dsh-plugin.com/p/cerebrixos-org/dsh-asimovbox/", + "category": "tools", + "description": { + "en": "Connects DeepSeek Harness to AsimovBox for creating, updating, rendering, and finishing videos through API-key-authenticated tools.", + "zh": "通过 API 密钥认证的工具将 DeepSeek Harness 连接到 AsimovBox,用于创建、更新、渲染和完成视频。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:cerebrixos-org/dsh-asimovbox", + "added": "2026-08-16" + }, + { + "name": "dsh-stage-gate", + "owner": "changingwang", + "url": "https://github.com/changingwang/dsh-stage-gate", + "page": "https://awesome-dsh-plugin.com/p/changingwang/dsh-stage-gate/", + "category": "tools", + "description": { + "en": "Stage-gate governance tools: model-callable gate_open / gate_check / gate_list / gate_close for in-memory, per-session acceptance checks with PASS/BLOCK conclusions.", + "zh": "阶段门治理工具:模型可直接调用的 gate_open / gate_check / gate_list / gate_close,为多阶段工作提供内存态、按会话隔离的验收核对与通过/阻塞结论。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:changingwang/dsh-stage-gate", + "added": "2026-08-18" + }, + { + "name": "dsh-econ-tools", + "owner": "Chaos-Hyper", + "url": "https://github.com/Chaos-Hyper/dsh-econ-tools", + "page": "https://awesome-dsh-plugin.com/p/Chaos-Hyper/dsh-econ-tools/", + "category": "tools", + "description": { + "en": "Econometrics research assistant: 6 tools covering method selection, data preparation, model specification, empirical analysis with Python/R/Stata code templates, robustness checks, and result reporting.", + "zh": "计量经济学研究助手:6 个工具覆盖方法选择、数据预处理、模型设定、实证分析(含 Python/R/Stata 代码模板)、稳健性检验与结果报告。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Chaos-Hyper/dsh-econ-tools", + "added": "2026-08-20" + }, + { + "name": "dsh-unidoc", + "owner": "Che-Year", + "url": "https://github.com/Che-Year/dsh-unidoc", + "page": "https://awesome-dsh-plugin.com/p/Che-Year/dsh-unidoc/", + "category": "tools", + "description": { + "en": "A document preview / edit / management plugin for DeepSeek Harness. It provides a VSCode-style \"Document Center\" workbench inside the DSH Web GUI: a file tree on the left, click-to-preview, editable code & Markdown with Ctrl/Cmd+S save; and exposes three document tools (doc_read / doc_edit / doc_create) to agents, letting models read and write workspace documents through natural language.", + "zh": "DeepSeek Harness 的文档预览 / 编辑 / 管理插件。在 DSH Web GUI 中提供一个 VSCode 风格的「文档中心」工作台:左侧文件树,点击即预览,代码与 Markdown 支持编辑与 Ctrl/Cmd+S 保存;同时为 Agent 暴露 doc_read / doc_edit / doc_create 三个文档工具,让模型可以通过自然语言读写工作区文档。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:Che-Year/dsh-unidoc", + "added": "2026-08-24" + }, + { + "name": "dsh-scan-mcp", + "owner": "chenbin-dev", + "url": "https://github.com/chenbin-dev/dsh-scan-mcp", + "page": "https://awesome-dsh-plugin.com/p/chenbin-dev/dsh-scan-mcp/", + "category": "tools", + "description": { + "en": "Scans MCP servers configured in Claude Code, Codex and CodeBuddy and probes real connectivity via stdio and streamable-http initialize handshakes, with enable/disable toggles and a /mcp control panel.", + "zh": "扫描 Claude Code / Codex / CodeBuddy 配置中的 MCP,通过 stdio 与 streamable-http initialize 握手检测真实连通性,支持启停开关与 /mcp 控制面板。" + }, + "npm": null, + "stars": null, + "downloads": null, + "install": "dsh plugin --profile web add github:chenbin-dev/dsh-scan-mcp", + "added": "2026-08-27" + }, + { + "name": "dsh-cache-precision", + "owner": "Cheng-cheng9669", + "url": "https://github.com/Cheng-cheng9669/dsh-cache-precision", + "page": "https://awesome-dsh-plugin.com/p/Cheng-cheng9669/dsh-cache-precision/", + "category": "tools", + "description": { + "en": "Renders the built-in cache-hit percentage with three decimals and widens the composer stats line to prevent truncation.", + "zh": "把内置缓存命中率原位显示为 3 位小数,并加宽底部统计行避免内容被截断。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:Cheng-cheng9669/dsh-cache-precision", + "added": "2026-08-15" + }, + { + "name": "dsh-proxy-routing", + "owner": "chenjiyan2001", + "url": "https://github.com/chenjiyan2001/dsh-proxy-routing", + "page": "https://awesome-dsh-plugin.com/p/chenjiyan2001/dsh-proxy-routing/", + "category": "tools", + "description": { + "en": "Per-execution HTTP/SOCKS5 proxy routing for Agent fetches, LLM providers, and shell commands, with settings-backed hot reload, approval-gated controls, and credential-safe status.", + "zh": "按每次执行为 Agent fetch、LLM provider 与 shell 命令路由 HTTP/SOCKS5 代理;支持 settings 热更新、审批门禁和凭据安全状态。" + }, + "npm": null, + "stars": 1, + "downloads": null, + "install": "dsh plugin --profile web add github:chenjiyan2001/dsh-proxy-routing", + "added": "2026-08-22" + }, + { + "name": "blender", + "owner": "CheshireJCat", + "url": "https://github.com/CheshireJCat/blender", + "page": "https://awesome-dsh-plugin.com/p/CheshireJCat/blender/", + "category": "tools", + "description": { + "en": "Blender 3D production plugin with 30 modeling/reconstruction skills, 13 runtime tools, and 26 deterministic helpers for reference fitting, rendering, validation, animation, and portable export; installable as `dsh-blender`.", + "zh": "Blender 3D 生产插件:提供 30 个建模/重建 Skill、13 个运行时工具和 26 个确定性 Helper,覆盖参考图拟合、渲染、验证、动画与可移植格式导出;npm 包名 `dsh-blender`。" + }, + "npm": "dsh-blender", + "stars": 20, + "downloads": 494, + "install": "dsh plugin --profile web add dsh-blender", + "added": "2026-08-14", + "screenshots": [ + "https://raw.githubusercontent.com/CheshireJCat/blender/main/assets/dsh-blender-lamp-preview.png" + ] + }, + { + "name": "dsh-mcp-bridge", + "owner": "Chhlafiu4312", + "url": "https://github.com/Chhlafiu4312/dsh-mcp-bridge", + "page": "https://awesome-dsh-plugin.com/p/Chhlafiu4312/dsh-mcp-bridge/", + "category": "tools", + "description": { + "en": "Zero-dependency MCP client bridge that connects stdio and HTTP MCP servers and registers their tools as mcp__ for the agent.", + "zh": "零依赖 MCP 客户端桥接,连接 stdio 与 HTTP MCP server,把其工具注册为 mcp__ 供 agent 调用。" + }, + "npm": null, + "stars": 4, + "downloads": null, + "install": "dsh plugin --profile web add github:Chhlafiu4312/dsh-mcp-bridge", + "added": "2026-08-18" + }, + { + "name": "cn-linebreak", + "owner": "chiang21fcb", + "url": "https://github.com/chiang21fcb/cn-linebreak", + "page": "https://awesome-dsh-plugin.com/p/chiang21fcb/cn-linebreak/", + "category": "tools", + "description": { + "en": "Static HTML auditor for Chinese line-breaking: checks explicit-`
` orphan lines, keep-all CSS coverage, CJK line-start/line-end punctuation rules, and protected-phrase splits, then inserts at natural breakpoints; exposes a cn_linebreak_audit tool for agents.", + "zh": "中文网页文案断行静态审查与修复工具:检查显式
孤字行、keep-all 覆盖、标点禁则、保护词被拆等问题,并在自然停顿处自动插入 ;提供 cn_linebreak_audit 工具供代理调用。" + }, + "npm": "cn-linebreak", + "stars": 1, + "downloads": 340, + "install": "dsh plugin --profile web add cn-linebreak", + "added": "2026-08-20" + }, + { + "name": "dsh-learning-mode#plugin", + "owner": "CHplus0", + "url": "https://github.com/CHplus0/dsh-learning-mode/tree/main/plugin", + "page": "https://awesome-dsh-plugin.com/p/CHplus0/dsh-learning-mode--plugin/", + "category": "tools", + "description": { + "en": "Learning Mode agent preset: a coding agent that teaches while coding — concrete scenario-grounded explanations, Socratic guidance, and TODO(你) practice blanks, modeled on Claude Code's Learning output style; installable via dsh plugin add (dsh-learning-mode on npm).", + "zh": "学习模式(Learning Mode)Agent 预设:边做边教的编码 Agent——具体的情景化阐释、苏格拉底式引导、TODO(你) 留白练习,模拟 Claude Code 的 Learning 输出风格;可 `dsh plugin add` 安装(npm 包 dsh-learning-mode)。" + }, + "npm": "dsh-learning-mode", + "stars": 6, + "downloads": 443, + "install": "dsh plugin --profile web add dsh-learning-mode", + "added": "2026-08-16" + }, + { + "name": "dsh-shell-command", + "owner": "CHplus0", + "url": "https://github.com/CHplus0/dsh-shell-command", + "page": "https://awesome-dsh-plugin.com/p/CHplus0/dsh-shell-command/", + "category": "tools", + "description": { + "en": "Claude Code-style shell integration: /! for single-command analysis and /terminal for interactive PTY sessions with on-demand history reference.", + "zh": "Claude Code 风格的 shell 集成:/! 单命令分析 与 /terminal 交互式终端(带按需历史引用)。" + }, + "npm": null, + "stars": 5, + "downloads": null, + "install": "dsh plugin --profile web add github:CHplus0/dsh-shell-command", + "added": "2026-08-19" + }, + { + "name": "dsh-plugin-midscene", + "owner": "ciky20171114", + "url": "https://github.com/ciky20171114/dsh-plugin-midscene", + "page": "https://awesome-dsh-plugin.com/p/ciky20171114/dsh-plugin-midscene/", + "category": "tools", + "description": { + "en": "AI UI automation via Midscene — android_ui and web_ui tools that tap, type, query, and assert on a real Android device or an already-running Chrome through the ctx.midscene seam.", + "zh": "基于 Midscene 的 AI UI 自动化——android_ui 与 web_ui 工具,通过 ctx.midscene seam 在真实 Android 设备或已运行的 Chrome 上完成点击、输入、查询与断言。" + }, + "npm": "dsh-plugin-midscene", + "stars": 2, + "downloads": 167, + "install": "dsh plugin --profile web add dsh-plugin-midscene", + "added": "2026-08-22" + }, + { + "name": "dsh-data-insight", + "owner": "ClaireXi99", + "url": "https://github.com/ClaireXi99/dsh-data-insight", + "page": "https://awesome-dsh-plugin.com/p/ClaireXi99/dsh-data-insight/", + "category": "tools", + "description": { + "en": "Data analysis toolkit: CSV/TSV/JSON profiling, IQR and z-score anomaly detection, structured summarization with quality issues, and code-free filter/group/aggregate queries.", + "zh": "数据分析工具包:CSV/TSV/JSON 数据画像、IQR 与 z-score 异常检测、带质量问题提示的结构化摘要、以及无需写代码的筛选/分组/聚合查询。" + }, + "npm": null, + "stars": 2, + "downloads": null, + "install": "dsh plugin --profile web add github:ClaireXi99/dsh-data-insight", + "added": "2026-08-17" + }, + { + "name": "dsh-interview", + "owner": "codingayice", + "url": "https://github.com/codingayice/dsh-interview", + "page": "https://awesome-dsh-plugin.com/p/codingayice/dsh-interview/", + "category": "tools", + "description": { + "en": "AI interview practice for DeepSeek Harness Web: generates questions across four modes (concept review, mock interview, scenario, LeetCode Hot 100), scores answers, and provides explanations.", + "zh": "面向 DeepSeek Harness Web 的 AI 面试陪练:支持背八股、模拟面试、场景题与力扣热题 100 四种模式,自动评分、点评并讲解知识点。" + }, + "npm": "dsh-interview", + "stars": 3, + "downloads": 612, + "install": "dsh plugin --profile web add dsh-interview", + "added": "2026-08-22" + }, + { + "name": "dsh-wechat-mp", + "owner": "coolbreezecoin", + "url": "https://github.com/coolbreezecoin/dsh-wechat-mp", + "page": "https://awesome-dsh-plugin.com/p/coolbreezecoin/dsh-wechat-mp/", + "category": "tools", + "description": { + "en": "Turns markdown into a WeChat Official Account (微信公众号) draft: inlines every CSS rule onto its element because the editor discards `` + +// ── landing + privacy pages (hand-written, copied with the count synced) ──── +for (const loc of LOCALES) { + for (const [src, urlPath] of [[loc.index, loc.urlPath], [loc.privacy, loc.privacyPath]]) { + if (!fs.existsSync(src)) { console.error(`${src} is missing`); process.exit(1) } + // The privacy page carries no catalog and no ad — it is the page that + // describes what the other pages load, so it stays plain. Resolving the + // tokens to '' there (rather than leaving them unhandled) means a token + // pasted into it by mistake renders as nothing instead of an ad. + const landing = src === loc.index + let page = fs.readFileSync(src, 'utf8') + .replaceAll('__COUNT__', () => String(plugins.length)) + .replaceAll('__BROWSE__', () => loc.browsePath) + .replaceAll('__HOME_CSS__', () => (landing ? homeCss() : '')) + .replaceAll('__HOME_CATALOG__', () => (landing ? homeCatalog(loc) : '')) + .replaceAll(AD_HEAD_TOKEN, () => (landing ? adHead() : '')) + page = applyStrings(page, loc) + if (urlPath === '/') fs.writeFileSync(`${OUT}/index.html`, page) + else write(urlPath, page) + } +} + +// ── browse index ──────────────────────────────────────────────────────────── +for (const loc of LOCALES) { + const byCat = CATS.map((id) => ({ + id, + name: catName(id, loc), + items: plugins.filter((p) => p.category === id).sort((a, b) => (b.stars ?? -1) - (a.stars ?? -1)), + })).filter((c) => c.items.length) + + const jump = byCat.map((c) => `${esc(c.name)}${c.items.length}`).join('\n ') + const sections = byCat.map((c) => `
+

${esc(c.name)}${c.items.length}

+ +
`).join('\n') + + let page = fs.readFileSync('site/browse-template.html', 'utf8') + .replaceAll('__LANG__', () => loc.htmlLang) + .replaceAll('__TITLE__', () => esc(loc.BROWSE_TITLE.replaceAll('{N}', plugins.length))) + .replaceAll('__DESC__', () => esc(loc.BROWSE_DESC.replaceAll('{N}', plugins.length))) + .replaceAll('__URL__', () => ORIGIN + loc.browsePath) + .replaceAll('__HREFLANGS__', () => hreflangs((l) => l.browsePath)) + .replaceAll('__OG_IMAGE__', () => `${ORIGIN}/assets/demo-${loc.code}.png`) + .replaceAll('__HOME__', () => loc.urlPath) + .replaceAll('__PRIVACY__', () => loc.privacyPath) + .replaceAll('__LOCALE_LINKS__', () => localeLinks(loc, (l) => l.browsePath)) + .replaceAll('__JUMP__', () => jump) + .replaceAll('__SECTIONS__', () => sections) + .replaceAll(AD_HEAD_TOKEN, () => adHead()) + write(loc.browsePath, applyStrings(page, loc)) +} + +// ── plugin pages ──────────────────────────────────────────────────────────── +const pluginMaster = fs.readFileSync('site/plugin-template.html', 'utf8') +let readmeCount = 0 +let mismatchCount = 0 +for (const loc of LOCALES) { + for (const p of plugins) { + const url = ORIGIN + pluginPath(loc, p.slug) + const desc = p.description?.[loc.code] ?? p.description?.en ?? '' + const metaDesc = desc.length > 155 ? desc.slice(0, 152).trimEnd() + '…' : desc + + const facts = [ + p.stars != null ? `${loc.strings.P_STARS} ★ ${p.stars}` : '', + `${loc.strings.P_CAT} ${esc(catName(p.category, loc))}`, + p.added ? `${loc.strings.P_ADDED} ${esc(p.added)}` : '', + p.npm ? `${loc.strings.P_NPM} ${esc(p.npm)}` : '', + ].filter(Boolean).join('\n ') + + const cmd = (c, note) => `

${note}

+
${esc(c)}
` + const install = [ + cmd('dsh plugin --profile web add dshmarket', loc.strings.P_INSTALL_MARKET), + cmd(p.install, loc.strings.P_INSTALL_CLI), + ].join('\n ') + + const shots = (p.screenshots ?? []).filter((s) => imgAllowed(s)) + const shotSection = shots.length ? `
+

${loc.strings.P_SHOTS}

+
+${shots.map((s) => ` `).join('\n')} +
+
` : '' + + const links = [ + `

${loc.strings.P_GH}

`, + p.npm ? `

${loc.strings.P_NPM_LINK}

` : '', + `

${loc.strings.P_CATALOG}

`, + ].filter(Boolean).join('\n ') + + // Cut the sidebar blurbs at a word rather than mid-token: "cloudflared + // tunne" reads as a broken page, not as a shortened sentence. CJK has no + // spaces, so there the hard cut stands and an ellipsis carries the meaning. + const blurb = (s, n = 90) => { + if (s.length <= n) return s + const head = s.slice(0, n) + const sp = head.lastIndexOf(' ') + return (sp > n * 0.6 ? head.slice(0, sp) : head).trimEnd() + '…' + } + const related = plugins + .filter((r) => r.category === p.category && r.url !== p.url) + .sort((a, b) => (b.stars ?? -1) - (a.stars ?? -1)) + .slice(0, 6) + const relSection = related.length ? `
+

${loc.strings.P_RELATED}

+
    +${related.map((r) => `
  • ${esc(r.displayName)}

    ${esc(blurb(r.description?.[loc.code] ?? r.description?.en ?? ''))}

  • `).join('\n')} +
+
` : '' + + // README: prefer this page's language, fall back to whatever exists, and + // mark the block with its real lang when they differ. A page that claims + // lang="en" around Chinese prose misinforms every consumer that reads it. + const entry = readmes[p.url] + let rm = null + let rmLang = null + if (entry) { + for (const code of [loc.code, ...LOCALES.map((l) => l.code)]) { + if (entry[code]) { rm = entry[code]; rmLang = code; break } + } + } + const readmeHtml = rm ? renderReadme(rm) : null + const mismatch = rm != null && rmLang !== loc.code + if (readmeHtml) readmeCount++ + if (mismatch) mismatchCount++ + const rmLocale = LOCALES.find((l) => l.code === rmLang) + const readmeSection = readmeHtml ? `
+

${loc.strings.P_README}

${mismatch ? `\n

${esc(loc.strings.P_README_ONLY.replace('{LANG}', loc.langNames[rmLang] ?? rmLang))}

` : ''} +
+${readmeHtml} +
+

${loc.strings.P_README_SRC}

+
` : '' + + // `mapping: specific` with the shared slug is what makes this page, the + // catalog's page and the market's drawer land on one discussion instead of + // three. `strict` hashes the term, so a slug that merely contains another + // cannot collide with it. + const commentsId = `comments-${p.slug.replace(/[^a-z0-9-]/gi, '-')}` + const commentsConfig = { + repo: COMMENTS.repo, + repoId: COMMENTS.repoId, + category: COMMENTS.category, + categoryId: COMMENTS.categoryId, + term: `plugin:${p.slug}`, + lang: loc.giscusLang, + } + const commentsSection = COMMENTS.enabled ? `
+

${loc.strings.P_COMMENTS}

+

${loc.strings.P_COMMENTS_NOTE}

+

+
+ +
` : '' + + const jsonld = JSON.stringify([{ + '@context': 'https://schema.org', + '@type': 'SoftwareApplication', + name: p.displayName, + url, + description: desc, + applicationCategory: 'DeveloperApplication', + operatingSystem: 'DeepSeek Harness', + offers: { '@type': 'Offer', price: 0, priceCurrency: 'USD' }, + sameAs: [p.url, p.npm ? `https://www.npmjs.com/package/${p.npm}` : null].filter(Boolean), + }, { + '@context': 'https://schema.org', + '@type': 'BreadcrumbList', + itemListElement: [ + { '@type': 'ListItem', position: 1, name: loc.strings.CRUMB_HOME, item: ORIGIN + loc.urlPath }, + { '@type': 'ListItem', position: 2, name: loc.strings.CRUMB_BROWSE, item: ORIGIN + loc.browsePath }, + { '@type': 'ListItem', position: 3, name: p.displayName, item: url }, + ], + }]) + + let page = pluginMaster + .replaceAll('__P_README_SECTION__', () => readmeSection) + .replaceAll('__P_COMMENTS__', () => commentsSection) + .replaceAll('__LANG__', () => loc.htmlLang) + .replaceAll('__TITLE__', () => esc(loc.P_TITLE.replace('{NAME}', p.displayName))) + .replaceAll('__DESC__', () => esc(metaDesc)) + .replaceAll('__URL__', () => url) + .replaceAll('__HREFLANGS__', () => hreflangs((l) => pluginPath(l, p.slug))) + .replaceAll('__OG_IMAGE__', () => `${ORIGIN}/assets/demo-${loc.code}.png`) + .replaceAll('__JSONLD__', () => ldSafe(jsonld)) + .replaceAll('__HOME__', () => loc.urlPath) + .replaceAll('__BROWSE__', () => loc.browsePath) + .replaceAll('__PRIVACY__', () => loc.privacyPath) + .replaceAll('__LOCALE_LINKS__', () => localeLinks(loc, (l) => pluginPath(l, p.slug))) + .replaceAll('__P_SHORT__', () => esc(p.name)) + .replaceAll('__P_H1__', () => `${esc(p.owner)}/${esc(p.name)}`) + .replaceAll('__P_DESC__', () => esc(desc)) + .replaceAll('__P_FACTS__', () => facts) + .replaceAll('__P_INSTALL__', () => install) + .replaceAll('__P_SHOTS__', () => shotSection) + .replaceAll('__P_LINKS__', () => links) + .replaceAll('__P_RELATED__', () => relSection) + .replaceAll(AD_HEAD_TOKEN, () => adHead()) + write(pluginPath(loc, p.slug), applyStrings(page, loc)) + } +} + +// ── sitemap ───────────────────────────────────────────────────────────────── +// lastmod is the date the underlying data last changed, not the date this file +// was written: a lastmod that moves nightly without the page changing teaches a +// crawler to stop believing every lastmod on the site. +const alt = (pathFor) => [ + ...LOCALES.map((l) => ` `), + ` `, +].join('\n') +const urlEntry = (pathFor, lastmod, freq) => LOCALES.map((l) => ` + ${ORIGIN}${pathFor(l)} + ${lastmod} + ${freq} +${alt(pathFor)} + `).join('\n') + +const updated = registry.updated ?? new Date().toISOString().slice(0, 10) +fs.writeFileSync(`${OUT}/sitemap.xml`, ` + +${urlEntry((l) => l.urlPath, updated, 'weekly')} +${urlEntry((l) => l.browsePath, updated, 'daily')} +${plugins.map((p) => urlEntry((l) => pluginPath(l, p.slug), p.added ?? updated, 'weekly')).join('\n')} +${urlEntry((l) => l.privacyPath, updated, 'yearly')} + +`) + +const pages = plugins.length * LOCALES.length + LOCALES.length * 3 +console.log(`site built: ${plugins.length} plugins × ${LOCALES.length} locales = ${pages} pages`) +console.log(` README bodies rendered: ${readmeCount} (${mismatchCount} in a fallback language)`) diff --git a/packages/dshmarket/scripts/check-web-auth-capture.mjs b/packages/dshmarket/scripts/check-web-auth-capture.mjs new file mode 100644 index 000000000..43b6515f3 --- /dev/null +++ b/packages/dshmarket/scripts/check-web-auth-capture.mjs @@ -0,0 +1,82 @@ +import { readFileSync, readdirSync } from 'node:fs' +import { resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +const ROOT = fileURLToPath(new URL('..', import.meta.url)) + +const FORBIDDEN = [ + { label: 'BrowserContext tracing', pattern: /\.\s*tracing\s*\./u }, + { label: 'HAR recording', pattern: /\brecordHar(?:Content|Mode|OmitContent|Path)?\b/u }, + { label: 'Playwright trace option', pattern: /\btrace\s*:\s*(?:true|['"`](?:on|retain-on-failure|on-first-retry)['"`])/u }, + { label: 'trace CLI flag', pattern: /--trace(?:=|\s)/u }, + { label: 'HAR CLI flag', pattern: /--(?:save-|record-)?har(?:=|\s)/iu }, + { label: 'trace environment switch', pattern: /\b(?:PLAYWRIGHT|PW_TEST)[A-Z_]*TRACE[A-Z_]*\b/u }, +] + +function browserSources(web = resolve(ROOT, 'tests', 'web')) { + const sources = [] + const visit = (directory) => { + for (const entry of readdirSync(directory, { withFileTypes: true })) { + const path = resolve(directory, entry.name) + if (entry.isDirectory()) { + if (entry.name !== 'fixtures') visit(path) + continue + } + if (!/\.(?:[cm]?[jt]sx?)$/u.test(entry.name) + || /\.(?:spec|test)\.(?:[cm]?[jt]sx?)$/u.test(entry.name)) continue + sources.push(path) + } + } + visit(web) + return sources +} + +function scan(label, text) { + const matches = FORBIDDEN + .filter(rule => rule.pattern.test(text)) + .map(rule => rule.label) + return matches.length === 0 ? [] : [`${label}: ${matches.join(', ')}`] +} + +export function checkAuthenticatedBrowserLane(paths = []) { + const failures = [] + if (paths.length > 0) { + for (const path of paths) failures.push(...scan(path, readFileSync(resolve(path), 'utf8'))) + return failures + } + + for (const path of [...browserSources(), resolve(ROOT, 'vitest.web.config.ts')]) { + failures.push(...scan(path, readFileSync(path, 'utf8'))) + } + + const packageJson = JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf8')) + const testWeb = packageJson?.scripts?.['test:web'] + if (typeof testWeb !== 'string' || !testWeb.includes('node scripts/check-web-auth-capture.mjs')) { + failures.push('package.json: test:web must run the authenticated-lane guard before Vitest') + } else { + failures.push(...scan('package.json scripts.test:web', testWeb)) + } + + const workflow = readFileSync(resolve(ROOT, '.github', 'workflows', 'ci.yml'), 'utf8') + if (!workflow.includes('npm run test:web')) { + failures.push('.github/workflows/ci.yml: Web E2E must run through npm run test:web') + } + failures.push(...scan('.github/workflows/ci.yml', workflow)) + return failures +} + +const invokedDirectly = process.argv[1] !== undefined + && resolve(process.argv[1]) === fileURLToPath(import.meta.url) +if (invokedDirectly) { + const args = process.argv.slice(2) + const sourceRoot = args[0] === '--browser-source-root' ? args[1] : undefined + const failures = sourceRoot === undefined + ? checkAuthenticatedBrowserLane(args) + : browserSources(resolve(sourceRoot)).flatMap(path => scan(path, readFileSync(path, 'utf8'))) + if (failures.length > 0) { + process.stderr.write(`authenticated browser capture guard failed:\n${failures.join('\n')}\n`) + process.exitCode = 1 + } else { + process.stdout.write('authenticated browser capture guard passed\n') + } +} diff --git a/packages/dshmarket/scripts/normalize-client-banner.mjs b/packages/dshmarket/scripts/normalize-client-banner.mjs new file mode 100644 index 000000000..b881d915f --- /dev/null +++ b/packages/dshmarket/scripts/normalize-client-banner.mjs @@ -0,0 +1,78 @@ +#!/usr/bin/env node +/** + * Post-build normalization of client/client.js, which is a COMMITTED and + * PUBLISHED artifact — so it must be byte-identical no matter whose machine + * built it: + * + * 1. Rolldown pretty-prints the tsdown banner into a three-line header, but + * the published contract (enforced by preflight.mjs, and relied on by + * hosts that sniff the loader id from the file head) requires the file to + * START with the exact one-line `window.__ModuleLoader__.load({ id: "…"` + * prefix. Collapse the header onto one line, leaving blank lines in place + * of the folded ones so the sourcemap's line numbers stay valid. + * 2. tsdown names the CSS module's virtual chunk by ABSOLUTE path, so the + * builder's checkout path (`/Users/…`, `D:\Github\…`) ends up committed + * in a region comment and shipped to npm. Besides leaking the path, it + * makes every contributor's artifact diff churn. Rewrite it to a stable + * repo-relative form. + * 3. The CSS module's class map is emitted in an UNSTABLE key order — two + * consecutive builds of identical source differ by ~265 lines. `prepare` + * runs the build on a plain `npm install`, so every contributor's working + * tree acquires that diff without touching anything, and three open PRs + * carry it. Sort the map by key; object key order has no effect on + * behaviour, and sorting preserves the line COUNT so the sourcemap's line + * numbers stay valid, exactly as the banner fold above relies on. + */ +import fs from 'node:fs' +import path from 'node:path' +import { sortClassMaps } from './sort-class-maps.mjs' + +const file = 'client/client.js' +const name = JSON.parse(fs.readFileSync('package.json', 'utf8')).name +const required = `window.__ModuleLoader__.load({ id: ${JSON.stringify(name)}, factory: (require) => {` + +let code = fs.readFileSync(file, 'utf8') + +// --- 1. one-line loader banner ------------------------------------------- +if (!code.startsWith(required)) { + const lines = code.split('\n') + const head = [ + 'window.__ModuleLoader__.load({', + `\tid: ${JSON.stringify(name)},`, + '\tfactory: (require) => {', + ] + if (lines[0] !== head[0] || lines[1] !== head[1] || lines[2] !== head[2]) { + console.error(`normalize-client-banner: unexpected ${file} header:\n` + lines.slice(0, 3).join('\n')) + process.exit(1) + } + lines[0] = required + lines[1] = '' + lines[2] = '' + code = lines.join('\n') +} + +// --- 2. machine-independent CSS virtual ids ------------------------------- +// `\0dsh-css:/src/client/Market.module.css.mjs` → `\0dsh-css:src/client/…` +const root = process.cwd().replaceAll('\\', '/') +const before = code +code = code.replace(/(dsh-css:)([^\n"]*?)(src[/\\][^\n"]*?\.css\.mjs)/g, (_all, prefix, _dir, rel) => + prefix + rel.replaceAll('\\', '/')) +// Guard: this builder's own checkout path must not survive anywhere, and no +// virtual id may stay absolute. Deliberately narrow — the bundle legitimately +// contains strings like `s:\` inside regexes, so a generic drive-letter scan +// would false-positive. +const leaks = [ + ...code.matchAll(new RegExp(root.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')), + ...code.matchAll(/dsh-css:(?:\/|[A-Za-z]:[/\\])[^\n"]*/g), +].map(match => match[0].slice(0, 60)) +if (leaks.length > 0) { + console.error(`normalize-client-banner: absolute build path left in ${file}: ${leaks.slice(0, 3).join(', ')}`) + process.exit(1) +} + +// --- 3. deterministic CSS class-map order --------------------------------- +const { code: ordered, sorted } = sortClassMaps(code) +code = ordered + +fs.writeFileSync(file, code) +console.log(`normalize-client-banner ok: ${file}${before === code ? '' : ' (paths normalized)'}${sorted > 0 ? ` (${sorted} class-map keys sorted)` : ''}`) diff --git a/packages/dshmarket/scripts/preflight.mjs b/packages/dshmarket/scripts/preflight.mjs new file mode 100644 index 000000000..f6352c6e4 --- /dev/null +++ b/packages/dshmarket/scripts/preflight.mjs @@ -0,0 +1,47 @@ +#!/usr/bin/env node +/** + * Pre-pack guard: every place that must carry the npm package name actually + * does. Both 1.0.0 (bundle patch name) and 1.0.1 (client loader id) shipped + * broken after the dsh-market → dshmarket rename because these are plain + * strings no compiler checks. + */ +import fs from 'node:fs' + +const name = JSON.parse(fs.readFileSync('package.json', 'utf8')).name + +const failures = [] + +const patch = fs.readFileSync('cordis.patch.yml', 'utf8') +if (!patch.includes(`name: '${name}'`)) { + failures.push(`cordis.patch.yml must insert by package name '${name}'`) +} + +const client = fs.readFileSync('client/client.js', 'utf8') +if (!client.startsWith(`window.__ModuleLoader__.load({ id: ${JSON.stringify(name)}`)) { + failures.push(`client/client.js must register __ModuleLoader__ id ${JSON.stringify(name)}`) +} + +/** + * A lockfile resolving to a mirror publishes fine and then fails in the + * consumer's install with EALLOWREMOTE, because a full tarball URL is not a + * registry name they can re-resolve. It reached us twice — 1.12.0 shipped + * with 61 npmmirror URLs and had its tag deleted and re-cut, then a single + * one came back the next time a lock was refreshed behind a mirror. Nothing + * about running `npm install` in China makes this visible locally, so the + * guard belongs where the package is built rather than in anyone's habits. + */ +const lock = fs.readFileSync('package-lock.json', 'utf8') +const foreign = [...new Set( + [...lock.matchAll(/"resolved": "(https?:\/\/[^/"]+)/g)] + .map(match => match[1]) + .filter(host => host !== 'https://registry.npmjs.org'), +)] +if (foreign.length > 0) { + failures.push(`package-lock.json resolves to ${foreign.join(', ')} — rewrite to https://registry.npmjs.org or consumers hit EALLOWREMOTE`) +} + +if (failures.length > 0) { + console.error('preflight failed:\n- ' + failures.join('\n- ')) + process.exit(1) +} +console.log(`preflight ok: ${name}`) diff --git a/packages/dshmarket/scripts/restart-smoke.mjs b/packages/dshmarket/scripts/restart-smoke.mjs new file mode 100644 index 000000000..0bb511aab --- /dev/null +++ b/packages/dshmarket/scripts/restart-smoke.mjs @@ -0,0 +1,47 @@ +#!/usr/bin/env node +import assert from 'node:assert/strict' +import { dirname, join, resolve } from 'node:path' +import { restartAllowed, restartLaunch, trustedRestartRequest } from '../lib/restart.js' + +function request(remoteAddress, origin = 'http://127.0.0.1:3080', host = '127.0.0.1:3080') { + return { socket: { remoteAddress }, headers: { origin, host } } +} + +assert.equal(trustedRestartRequest(request('127.0.0.1')), true) +assert.equal(trustedRestartRequest(request('::1', 'http://localhost:3080', 'localhost:3080')), true) +assert.equal(trustedRestartRequest(request('::ffff:127.0.0.1')), true) +assert.equal(trustedRestartRequest(request('192.168.1.2')), false) +assert.equal(trustedRestartRequest(request('127.0.0.1', 'http://evil.example', '127.0.0.1:3080')), false) +assert.equal(trustedRestartRequest(request('127.0.0.1', 'file://127.0.0.1:3080', '127.0.0.1:3080')), false) +assert.equal(trustedRestartRequest({ + ...request('127.0.0.1'), + headers: { ...request('127.0.0.1').headers, 'x-forwarded-for': '127.0.0.1' }, +}), false) + +// Explicitly parameterised rather than reading the ambient environment: +// this script runs on CI runners, which ARE systemd units, so their jobs +// inherit INVOCATION_ID. An ambient `restartAllowed({})` therefore asserts +// something different on a runner than on a laptop — which is exactly how +// this caught the first, too-loose version of the detection (#229). +assert.equal(restartAllowed({}, {}, 4242), true) +assert.equal(restartAllowed({ allowRestart: true }, {}, 4242), true) +assert.equal(restartAllowed({ allowRestart: false }, {}, 4242), false) +// Supervised: marker AND being the unit's own main process (ppid 1). +assert.equal(restartAllowed({}, { INVOCATION_ID: 'x' }, 1), false) +// Inherited marker under an ordinary parent must NOT read as supervised. +assert.equal(restartAllowed({}, { INVOCATION_ID: 'x' }, 4242), true) + +const originalArgv = [...process.argv] +try { + const entry = join('apps', 'cli', 'src', 'bin.ts') + process.argv.splice(0, process.argv.length, process.execPath, entry, 'web', '--port', '3081') + const launch = restartLaunch() + assert.equal(launch.file, process.execPath) + assert.deepEqual(launch.args, [...process.execArgv, resolve(entry), 'web', '--port', '3081']) + assert.equal(launch.cwd, dirname(resolve(entry))) + assert.equal(launch.viaShell, false) +} finally { + process.argv.splice(0, process.argv.length, ...originalArgv) +} + +console.log('restart smoke ok: guarded route and resolved DSH launch') diff --git a/packages/dshmarket/scripts/smoke-spawn.mjs b/packages/dshmarket/scripts/smoke-spawn.mjs new file mode 100644 index 000000000..496f0638a --- /dev/null +++ b/packages/dshmarket/scripts/smoke-spawn.mjs @@ -0,0 +1,46 @@ +#!/usr/bin/env node +/** + * Cross-platform smoke for the tool-spawning pattern used by routes.ts. + * On Windows, npm/corepack/pnpm are `.cmd` shims that node:child_process + * cannot start without a shell (#2/#3/#5) — this guards the cmd.exe shim + * path from regressing. Runs in CI on windows-latest and ubuntu-latest. + */ +import { spawn } from 'node:child_process' + +const CMD_METACHARS = /[\s"&|<>^()%!]/ + +const quoteCmdArg = (arg) => ( + CMD_METACHARS.test(arg) ? `"${arg.replace(/"/g, '""')}"` : arg +) + +function probe(file, args) { + return new Promise((resolve) => { + let child + if (process.platform === 'win32') { + const command = [file, ...args].map(quoteCmdArg).join(' ') + child = spawn(process.env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', `"${command}"`], { + stdio: 'ignore', + windowsVerbatimArguments: true, + }) + } else { + child = spawn(file, args, { stdio: 'ignore' }) + } + child.on('error', () => resolve(false)) + child.on('close', (code) => resolve(code === 0)) + }) +} + +const results = await Promise.all([ + probe('npm', ['--version']), + probe('pnpm', ['--version']), +]) + +if (!results[0]) { + console.error(`smoke failed on ${process.platform}: spawn npm --version did not exit 0`) + process.exit(1) +} +if (!results[1]) { + console.error(`smoke failed on ${process.platform}: spawn pnpm --version did not exit 0 (is pnpm set up in CI?)`) + process.exit(1) +} +console.log(`smoke ok on ${process.platform}: npm + pnpm spawn via ${process.platform === 'win32' ? 'cmd.exe shim' : 'direct spawn'}`) diff --git a/packages/dshmarket/scripts/sort-class-maps.mjs b/packages/dshmarket/scripts/sort-class-maps.mjs new file mode 100644 index 000000000..217529007 --- /dev/null +++ b/packages/dshmarket/scripts/sort-class-maps.mjs @@ -0,0 +1,65 @@ +/** + * Deterministic ordering for the CSS module class map inside the built + * client bundle. + * + * tsdown emits that map in an unstable key order: two consecutive builds of + * identical source differed by ~265 lines. `prepare` runs the build on a + * plain `npm install`, so every contributor's working tree acquired the diff + * without them touching anything, and three open PRs carried it — a + * guaranteed conflict on a generated file. + * + * Key order has no effect on behaviour, and sorting preserves the line COUNT, + * so the sourcemap's line numbers stay valid — the same property the banner + * fold in normalize-client-banner.mjs depends on. + */ + +/** + * One `"key": "value"` line, with the trailing comma optional because the + * last entry of an object literal has none. Excluding it would leave + * whichever key randomly landed last pinned in place while everything else + * sorted around it, which is still a per-build diff. + */ +const CLASS_LINE = /^(\s*)"([A-Za-z0-9_$]+)": "([^"]+)",?$/ + +/** + * Sort every CSS-module class map in `code`, leaving all other object + * literals untouched. + * + * A run qualifies only when every value is exactly `` under one + * shared prefix and one shared indent. That is the CSS module's own shape; + * the bundle's locale tables are `"key": "翻译"`, so a looser "sort any + * object literal" rule would reorder those too — a far larger diff, and one + * that would fight anyone reading the bundle against the source. + * @param {string} code the built bundle + * @returns {{ code: string, sorted: number }} rewritten bundle and key count + */ +export function sortClassMaps(code) { + const lines = code.split('\n') + let sorted = 0 + for (let start = 0; start < lines.length; start++) { + const first = CLASS_LINE.exec(lines[start] ?? '') + if (first === null || !first[3].endsWith(first[2]) || first[3] === first[2]) continue + const prefix = first[3].slice(0, first[3].length - first[2].length) + let end = start + while (end + 1 < lines.length) { + const next = CLASS_LINE.exec(lines[end + 1] ?? '') + if (next === null || next[1] !== first[1] || next[3] !== prefix + next[2]) break + end++ + } + // A single line is already in a deterministic order by definition. + if (end > start) { + const entries = lines.slice(start, end + 1).map(line => CLASS_LINE.exec(line)) + // Which positions carry a comma is a property of the POSITION, not of + // the entry that happened to sit there, so it is re-applied after the + // sort rather than carried along with the key. + const commas = lines.slice(start, end + 1).map(line => line.endsWith(',')) + entries.sort((a, b) => (a?.[2] ?? '').localeCompare(b?.[2] ?? '', 'en')) + const run = entries.map((entry, index) => + `${entry?.[1] ?? ''}"${entry?.[2] ?? ''}": "${entry?.[3] ?? ''}"${commas[index] === true ? ',' : ''}`) + lines.splice(start, run.length, ...run) + sorted += run.length + } + start = end + } + return { code: sorted > 0 ? lines.join('\n') : code, sorted } +} diff --git a/packages/dshmarket/scripts/validate-registry.mjs b/packages/dshmarket/scripts/validate-registry.mjs new file mode 100644 index 000000000..d304b1ed8 --- /dev/null +++ b/packages/dshmarket/scripts/validate-registry.mjs @@ -0,0 +1,282 @@ +#!/usr/bin/env node +/** + * Registry data gate — P1-8 in IMPROVEMENT-PLAN.md ("目录数据治理"). + * + * Offline, dependency-free validation of data/registry-snapshot.json, the + * curated plugin catalog the market serves. It catches the defects that would + * otherwise reach users as a broken install or an ambiguous entry: + * + * E1 required string fields present and non-empty + * E2 description carries both non-empty "en" and "zh" (the market is bilingual) + * E3 npm is null or a syntactically valid npm package name + * E4 stars is a non-negative integer, or null when the catalog has no count + * E5 url is a github repo or tree(subpath) url + * E6 owner field matches the owner parsed from url + * E7 category is in the declared whitelist (top-level `categories`) + * E8 page is an awesome-dsh-plugin.com/p/ url + * E9 added is a YYYY-MM-DD date not in the future + * E10 install command references the entry's real target (npm name, + * github:owner/repo[+ #path:], or a release archive under that same repo) + * E11 no duplicate install identity (same repo+subpath, or same npm package) + * E12 top-level `count` matches plugins.length + * + * Network existence probes (does the npm package / github repo actually exist) + * are intentionally out of scope: they need network, are slow, and are flaky in + * CI. They belong in a separate `--probe` maintainer tool, not the merge gate. + * + * Exit code: 0 when there are no hard errors, 1 otherwise. Warnings never fail + * the run — they surface informational signal (display-name collisions across + * distinct authors/monorepos are permitted by design). + */ + +import fs from 'node:fs' +import { fileURLToPath } from 'node:url' + +const SNAPSHOT = fileURLToPath(new URL('../data/registry-snapshot.json', import.meta.url)) + +// npm package-name syntax (scoped or simple), lowercased per npm rules. +const NPM_NAME_RE = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/ +// repo root, or a /tree// monorepo subpackage. +const GITHUB_URL_RE = /^https:\/\/github\.com\/([^/]+)\/([^/]+)(\/tree\/[^/]+\/(.+))?$/ +const PAGE_RE = /^https:\/\/awesome-dsh-plugin\.com\/p\// +const DATE_RE = /^\d{4}-\d{2}-\d{2}$/ +const INSTALL_RE = /^dsh plugin --profile \S+ add (.+)$/ + +const now = new Date() + +/** + * The `owner/repo` a GitHub release archive belongs to, or null when the + * target is not one. Mirrors releaseTarballTarget in src/sources.ts. + * @param target - the install target from the entry's install command. + * @returns lowercase `owner/repo`, or null. + */ +function releaseTargetRepo(target) { + let url + try { url = new URL(target) } catch { return null } + if (url.protocol !== 'https:' || url.hostname !== 'github.com') return null + if (!url.pathname.endsWith('.tgz') && !url.pathname.endsWith('.tar.gz')) return null + const segments = url.pathname.split('/').filter(segment => segment !== '') + if (segments.length < 4 || segments[2] !== 'releases') return null + return `${segments[0]}/${segments[1]}`.toLowerCase() +} + +function fail(errors, entry, code, msg) { + errors.push({ entry: entry && entry.name ? entry.name : '', code, msg }) +} + +function main() { + const errors = [] + const warnings = [] + + let raw + try { + raw = JSON.parse(fs.readFileSync(SNAPSHOT, 'utf8')) + } catch (e) { + console.error(`validate-registry: cannot read ${SNAPSHOT}: ${e.message}`) + process.exit(1) + } + + const plugins = raw && raw.plugins + if (!Array.isArray(plugins)) { + console.error('validate-registry: snapshot.plugins is not an array') + process.exit(1) + } + + const categories = new Set(Object.keys((raw && raw.categories) || {})) + const requiredStrings = ['name', 'owner', 'url', 'page', 'category', 'install', 'added'] + + const identSeen = new Map() // true install identity -> first entry name + const nameToEntries = new Map() // display name -> [owners] + let subpathCount = 0 + + for (const p of plugins) { + // E1 — required string fields + for (const f of requiredStrings) { + if (typeof p[f] !== 'string' || p[f].length === 0) { + fail(errors, p, 'E1', `missing or empty string field "${f}"`) + } + } + + // E2 — bilingual description + if ( + typeof p.description !== 'object' || p.description === null || + typeof p.description.en !== 'string' || p.description.en.length === 0 || + typeof p.description.zh !== 'string' || p.description.zh.length === 0 + ) { + fail(errors, p, 'E2', 'description must be an object with non-empty "en" and "zh" strings') + } + + // E3 — npm type & syntax + if (!(p.npm === null || typeof p.npm === 'string')) { + fail(errors, p, 'E3', 'npm must be null or a string') + } else if (p.npm && !NPM_NAME_RE.test(p.npm)) { + fail(errors, p, 'E3', `npm "${p.npm}" is not a valid npm package name`) + } + + // E4 — stars + // + // null is a legitimate value, not a defect: the catalog probes star counts + // against the GitHub API, whose anonymous quota it can exhaust, and its own + // publish guard ships as long as two thirds of entries have a count. So up + // to a third of a perfectly good plugins.json can carry `stars: null`, and + // the market renders those cards without the star line already + // (`typeof p.stars === 'number' &&`). Requiring a number here made this + // check pass only by luck — on the snapshot refresh where 302 entries came + // back null it failed the build for data that was doing nothing wrong. + if (p.stars !== null && p.stars !== undefined + && (typeof p.stars !== 'number' || !Number.isInteger(p.stars) || p.stars < 0)) { + fail(errors, p, 'E4', `stars must be a non-negative integer or null, got ${JSON.stringify(p.stars)}`) + } + + // E5 — url shape (repo root or tree subpath) + const um = typeof p.url === 'string' ? p.url.match(GITHUB_URL_RE) : null + if (!um) { + fail(errors, p, 'E5', `url is not a github repo/tree url: ${p.url}`) + } else if (um[4] !== undefined) { + subpathCount++ + } + + // E6 — owner matches url owner + if (um && p.owner !== um[1]) { + fail(errors, p, 'E6', `owner "${p.owner}" does not match url owner "${um[1]}"`) + } + + // E7 — category whitelist + if (!categories.has(p.category)) { + fail(errors, p, 'E7', `category "${p.category}" is not in the declared whitelist`) + } + + // E8 — page url + if (typeof p.page !== 'string' || !PAGE_RE.test(p.page)) { + fail(errors, p, 'E8', `page is not an awesome-dsh-plugin url: ${p.page}`) + } + + // E9 — added date, not future + if (typeof p.added !== 'string' || !DATE_RE.test(p.added)) { + fail(errors, p, 'E9', `added "${p.added}" is not YYYY-MM-DD`) + } else { + const dt = new Date(p.added + 'T00:00:00Z') + if (Number.isNaN(dt.getTime())) fail(errors, p, 'E9', `added "${p.added}" is not a valid date`) + else if (dt > now) fail(errors, p, 'E9', `added "${p.added}" is in the future`) + } + + // E10 — install references the real target + const im = typeof p.install === 'string' ? p.install.match(INSTALL_RE) : null + if (!im) { + fail(errors, p, 'E10', `install must match "dsh plugin --profile

add ": ${p.install}`) + } else { + // The command quotes a URL target, so the quotes are part of the match. + const target = im[1].replace(/^"(.*)"$/, '$1') + if (p.npm) { + if (target !== p.npm) fail(errors, p, 'E10', `install target "${target}" does not match npm "${p.npm}"`) + } else if (um) { + const expect = `github:${um[1]}/${um[2]}` + // An author-published release archive is a legitimate target — it is + // the prebuilt path that makes a source-only plugin install in + // seconds. What matters is the same thing the market enforces at + // install time (releaseTarballTarget in src/sources.ts): the archive + // must live under the entry's OWN repo, or an entry could name a + // trusted repository and serve bytes from somewhere else. Kept in + // step with that function deliberately, including the case-insensitive + // owner/repo compare and the refusal of the release CDN hosts, whose + // paths carry no owner to bind to. + const release = releaseTargetRepo(target) + if (release !== null) { + if (release !== `${um[1]}/${um[2]}`.toLowerCase()) { + fail(errors, p, 'E10', `release archive "${target}" belongs to ${release}, not ${um[1]}/${um[2]}`) + } + } else if (!target.startsWith(expect)) { + fail(errors, p, 'E10', `install target "${target}" does not reference ${expect}`) + } + } + } + + // E12 — screenshots (optional): 1-8 https URLs on GitHub image hosting. + // Mirrors the upstream build gate — a snapshot must never carry an image + // URL that would let a compromised registry plant a tracking pixel in + // every market user's browser (#61). + if (p.screenshots !== undefined) { + const SHOT_HOSTS = new Set(['raw.githubusercontent.com', 'user-images.githubusercontent.com', 'camo.githubusercontent.com', 'github.com']) + if (!Array.isArray(p.screenshots) || p.screenshots.length === 0 || p.screenshots.length > 8) { + fail(errors, p, 'E12', 'screenshots must be an array of 1-8 URLs when present') + } else { + for (const shot of p.screenshots) { + let parsed = null + try { parsed = new URL(String(shot)) } catch { /* fails below */ } + if (parsed === null || parsed.protocol !== 'https:' || !SHOT_HOSTS.has(parsed.hostname)) { + fail(errors, p, 'E12', `screenshot must be an https URL on GitHub hosting: ${String(shot)}`) + } + } + } + } + + // E11 — true install-identity uniqueness + let identity + if (p.npm) { + identity = `npm:${p.npm}` + } else if (um) { + // The subpath comes from `url`, which every entry has, rather than from + // the install command, which only carries `#path:` for the github: + // shortcut form. An entry that installs from a release archive has no + // such fragment, so reading the command collapsed every plugin in one + // monorepo onto a single identity and reported unrelated plugins as + // duplicates of each other. + const sp = um[4] ?? (im && (im[1].match(/#path:\/(.+)$/) || [])[1]) ?? '' + identity = `gh:${um[1]}/${um[2]}#${sp}` + } else { + identity = `??:${p.url}` + } + if (identSeen.has(identity)) { + fail(errors, p, 'E11', `duplicate install identity with "${identSeen.get(identity)}" (same repo+subpath or npm package)`) + } else { + identSeen.set(identity, p.name) + } + + // W1 — display-name collision (allowed across distinct authors / monorepos) + const seen = nameToEntries.get(p.name) + if (seen) seen.push(p.owner) + else nameToEntries.set(p.name, [p.owner]) + } + + // E12 — count integrity + if (typeof raw.count === 'number' && raw.count !== plugins.length) { + fail(errors, null, 'E12', `top-level count ${raw.count} != plugins.length ${plugins.length}`) + } + + // Assemble warnings + let nameCollisionGroups = 0 + for (const [name, owners] of nameToEntries) { + if (owners.length > 1) { + nameCollisionGroups++ + warnings.push(`display name "${name}" used by ${owners.length} entries (owners: ${owners.join(', ')}); allowed, but worth a glance`) + } + } + const gitOnly = plugins.filter((p) => p.npm === null).length + const npmPublished = plugins.length - gitOnly + const stars0 = plugins.filter((p) => p.stars === 0).length + + // Report + const rel = SNAPSHOT.replace(/\\/g, '/').replace(/.*dsh-market\//, 'dsh-market/') + console.log(`validate-registry: ${rel}`) + console.log( + ` plugins: ${plugins.length} | categories: ${categories.size} | ` + + `npm-published: ${npmPublished} | git-only: ${gitOnly} | monorepo-subpath: ${subpathCount}` + ) + + if (errors.length > 0) { + console.error(`\n ${errors.length} error(s):`) + for (const e of errors) { + console.error(` [${e.code}] ${e.entry}: ${e.msg}`) + } + console.log(`\n warnings: ${warnings.length} (display-name collisions: ${nameCollisionGroups}, stars=0: ${stars0})`) + process.exit(1) + } + + console.log(` warnings: ${warnings.length} (display-name collisions: ${nameCollisionGroups}, stars=0: ${stars0})`) + if (warnings.length > 0) { + for (const w of warnings) console.log(` - ${w}`) + } + console.log('\n registry ok ✓') +} + +main() diff --git a/packages/dshmarket/src/accelerate.ts b/packages/dshmarket/src/accelerate.ts new file mode 100644 index 000000000..de8cc6e5b --- /dev/null +++ b/packages/dshmarket/src/accelerate.ts @@ -0,0 +1,167 @@ +/** + * Resolving a GitHub install through a region's proxy without proxying the + * tarball pnpm records. + * + * pnpm does not fetch `github:owner/repo` with `git clone`; it resolves the + * shortcut and downloads a tarball from codeload.github.com. That rules out + * the usual `git config insteadOf` trick — there is no git command to + * redirect. + * + * Prefix-proxying that tarball used to save substantial time, but pnpm 11's + * fail-closed lockfile checks changed the safety boundary. A URL shaped like + * `https://proxy/https://codeload.github.com/.../` no longer has the + * codeload hostname, so pnpm treats it as an ordinary remote tarball and + * requires an integrity hash that its GitHub resolver does not write. The + * result is ERR_PNPM_MISSING_TARBALL_INTEGRITY on the install itself (#385). + * Disabling that check or minting a checksum from whatever the proxy served + * would weaken pnpm's supply-chain protection, so neither is acceptable. + * + * The safe middle ground is to resolve HEAD through the regional proxy, then + * hand pnpm a commit-pinned `github:owner/repo#` target. The ref lookup + * still avoids a slow or unreachable GitHub metadata request; pnpm itself + * fetches the canonical codeload URL, records it as `gitHosted: true`, and + * keeps its integrity policy intact. + * + * - **The commit has to be pinned.** The profile reads each plugin's + * installed commit back out of the lockfile by matching a codeload URL + * ending in a 40-character SHA (src/profile.ts). A `HEAD` tarball installs + * perfectly and then reports no version forever. So this resolves the SHA + * first, and a rewrite that cannot get one does not happen. + * - **Build-script approval has to keep matching.** `gitAllowBuildsKey` + * (src/sources.ts) derives its stable key from the repo, and the pinned + * GitHub form preserves that identity. + * + * Catalog subpath entries are left alone: `#path:` identifies a different + * package inside the repo, so the acceleration step never rewrites that + * fragment. Collection roots discovered after install preserve the resolved + * commit and add their subpath as pnpm's second `&path:` selector. + * + * Every SHA-lookup failure falls back to the original target. Acceleration + * is an optimisation, and an optimisation that can fail an install is a bug. + */ + +import { logEvent } from './log.ts' +import { marketFetch } from './net.ts' +import { routesFor, type Region } from './regions.ts' + +/** A bare repo shortcut whose HEAD can be replaced by one immutable ref. */ +const BARE_GITHUB_RE = /^github:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)$/ + +/** + * How long to wait for the SHA before giving up and installing directly. + * + * Short on purpose: this is spent BEFORE the download starts, and the whole + * point is to avoid a slow or unreachable direct metadata lookup. A proxy + * that cannot answer promptly should not delay pnpm's ordinary direct path. + */ +const RESOLVE_TIMEOUT_MS = 6000 + +/** + * The commit `HEAD` points at, from git's own ref advertisement. + * + * This is the endpoint `git clone` reads before it fetches anything, and it + * is the right one here for two measured reasons. It is not the REST API, so + * it does not consume the 60-requests-per-hour unauthenticated quota that a + * user installing a handful of plugins could plausibly exhaust. And it + * survives the proxy: over five consecutive tries from an unproxied mainland + * connection it answered 200 in ~1.2s every time, while the REST API through + * the same proxy returned 200, 200, then 403 — a proxy that rate-limits the + * API path would silently drop every install back to the slow route. + * + * The response is git's pkt-line format, whose first ref line carries + * ` HEAD\0`. Read with a pattern rather than a parser: + * one 40-character hex string followed by `HEAD` is unambiguous in this + * payload, and a length-prefix reader would be more code to get wrong. + * + * The same advertisement lists every branch and tag, which is what `ref` + * uses. A plugin installed from `github:owner/repo#publish` is not asking + * about the default branch at all, and answering with it made the market + * report an update forever (#446) — the two SHAs simply never match. Both + * namespaces are searched because pnpm accepts a tag there too, branches + * first since that is what the reports are about. + * @param ref - branch or tag to resolve, or undefined for the default branch. + */ +export async function headCommit( + repo: string, + proxy: string | null, + signal?: AbortSignal, + ref?: string, +): Promise { + const base = `https://github.com/${repo}/info/refs?service=git-upload-pack` + try { + const res = await marketFetch(proxy === null ? base : `${proxy}/${base}`, { + signal, + headers: { 'user-agent': 'git/2.40.0' }, + }) + if (!res.ok) return null + const body = await res.text() + if (ref === undefined) { + const found = /([0-9a-f]{40}) HEAD/.exec(body) + return found === null ? null : found[1]! + } + // Anchored to the ref's full name so `publish` cannot match a branch + // called `publish-old`, and escaped because a ref may contain regex + // metacharacters (`.` is legal in a branch name). + const quoted = ref.replace(/[.*+?^${}()|[\]\\]/gu, String.raw`\$&`) + for (const namespace of ['heads', 'tags']) { + const found = new RegExp(String.raw`([0-9a-f]{40}) refs/${namespace}/${quoted}(?![^\s])`, 'u').exec(body) + if (found !== null) return found[1]! + } + return null + } catch { + return null + } +} + +/** + * The current `HEAD` commit for a repo, on whichever route the region uses. + * + * Wraps the timeout so callers outside the install path — the build-script + * approval below, which needs a commit-pinned key — do not each reinvent it. + */ +export async function resolveHeadCommit( + repo: string, + region: Region, + env: NodeJS.ProcessEnv = process.env, +): Promise { + const controller = new AbortController() + const timer = setTimeout(() => { controller.abort() }, RESOLVE_TIMEOUT_MS) + try { + return await headCommit(repo, routesFor(region, env).githubProxy, controller.signal) + } finally { + clearTimeout(timer) + } +} + +/** + * The install target to actually hand pnpm, given the region in force. + * + * @param target - what `installTargetFor` produced. + * @param region - the download region. + * @param env - environment, for the proxy override. + * @returns a commit-pinned GitHub shortcut when the mirror resolves HEAD, + * otherwise `target` unchanged. + */ +export async function acceleratedTarget( + target: string, + region: Region, + env: NodeJS.ProcessEnv = process.env, +): Promise { + const proxy = routesFor(region, env).githubProxy + if (proxy === null) return target + const bare = BARE_GITHUB_RE.exec(target) + if (bare === null) return target + const repo = bare[1]! + const controller = new AbortController() + const timer = setTimeout(() => { controller.abort() }, RESOLVE_TIMEOUT_MS) + try { + const sha = await headCommit(repo, proxy, controller.signal) + if (sha === null) { + logEvent('info', 'region', `${repo}: could not resolve a commit through the mirror; installing directly`) + return target + } + return `github:${repo}#${sha}` + } finally { + clearTimeout(timer) + } +} diff --git a/packages/dshmarket/src/agents.ts b/packages/dshmarket/src/agents.ts new file mode 100644 index 000000000..d6d4a0dad --- /dev/null +++ b/packages/dshmarket/src/agents.ts @@ -0,0 +1,43 @@ +/** + * Optional host-provided agent inventory: lets the market refuse plugin + * mutations while a live agent is mid-turn. Mutating `node_modules` under a + * running agent replaces files the running plugin modules may still read or + * lazily import — the update "succeeds" while old code and new files mix. + */ + +export interface RunningAgentLike { + id?: unknown + status?: unknown +} + +export interface AgentsServiceLike { + list(): RunningAgentLike[] +} + +export type AgentsLookup = () => AgentsServiceLike | undefined + +/** + * Ids of the host's currently running agents, or [] when the host has no + * agents service. Only a positive `status === 'running'` blocks an update — + * unknown statuses are treated as not running, so a future agent + * implementation with different wording fails open (the market stays + * usable) instead of wedging the plugin page. + */ +export function runningAgentIds(agents: AgentsServiceLike | undefined): string[] { + if (agents === undefined) return [] + let listed: RunningAgentLike[] + try { + listed = agents.list() + } catch { + // A half-disposed registry must never take the market's routes down. + return [] + } + if (!Array.isArray(listed)) return [] + const ids: string[] = [] + for (const agent of listed) { + if (agent === null || typeof agent !== 'object' || agent.status !== 'running') continue + const id = typeof agent.id === 'string' && agent.id !== '' ? agent.id : 'agent' + if (!ids.includes(id)) ids.push(id) + } + return ids +} diff --git a/packages/dshmarket/src/backup.ts b/packages/dshmarket/src/backup.ts new file mode 100644 index 000000000..1918839f7 --- /dev/null +++ b/packages/dshmarket/src/backup.ts @@ -0,0 +1,542 @@ +/** + * Portable profile backups: configuration only, never installed packages. + * + * The profile directory is plain user data — aside from package.json it can + * hold API keys (config.toml), tokens, or the WebDAV password when stored + * server-side. Backups therefore behave like `dsh export` and carry the same + * credential-warning disclaimer in the UI (review #63). + */ + +import { + existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, +} from 'node:fs' +import { lookup } from 'node:dns/promises' +import { request as httpsRequest } from 'node:https' +import { isIP } from 'node:net' +import { dirname, isAbsolute, relative, resolve, sep } from 'node:path' +import { profileDir } from './profile.ts' + +export const BACKUP_FORMAT = 'dsh-profile-backup' +export const MAX_BACKUP_BYTES = 2 * 1024 * 1024 +const MAX_FILES = 256 +const SKIP_NAMES = new Set(['node_modules', '.dsh-market', '.git', 'pnpm-lock.yaml']) +/** File names that routinely contain credentials (backup exports). + * Values are never masked in place — the export is one-to-one for + * faithful restores — but presence is surfaced by the UI warning. */ +export const SECRET_FILE_HINTS = /(^|\/)(config\.toml|\.env(\.\w+)?|secrets?\.\w+t?j?s?o?n|pnpm-workspace\.yaml)$/i + +/** Count of exported files whose names look like they carry credentials. */ +export function secretFileCount(profile: string, explicitDir?: string): number { + let count = 0 + for (const path of profileFiles(explicitDir ?? profileDir(profile))) { + if (SECRET_FILE_HINTS.test(path)) count += 1 + } + return count +} + +export type BackupFile = + | { path: 'package.json'; json: Record } + | { path: string; lines: string[] } + +export interface ProfileBackup { + format: typeof BACKUP_FORMAT + version: 0.2 + createdAt: string + profile: string + files: BackupFile[] +} + +function profileFiles(root: string, dir = root): string[] { + const files: string[] = [] + for (const entry of readdirSync(dir, { withFileTypes: true })) { + // Any .bak marker, not just the numeric-suffixed one this codebase + // writes. Recovery and the host's own repair paths leave other shapes — + // `package.json.bak-asm`, `cordis.patch.yml.rp-merged.bak` — and a + // backup that carried them restored them too, so the profile came back + // with the wreckage that made it need repairing (#205 by @Rudyy898). + if (SKIP_NAMES.has(entry.name) || /\.bak\b/.test(entry.name)) continue + const path = resolve(dir, entry.name) + if (entry.isSymbolicLink()) continue + if (entry.isDirectory()) files.push(...profileFiles(root, path)) + else if (entry.isFile()) files.push(relative(root, path).split(sep).join('/')) + if (files.length > MAX_FILES) throw new Error(`profile has more than ${MAX_FILES} configuration files`) + } + return files +} + +/** Serialize every profile file except dependencies, lock state, and market cache. */ +export interface BackupOptions { + /** Partial export: only these plugins (dependency names) are kept. */ + includeDeps?: string[] + /** + * With includeDeps, also carry the profile's other configuration files. + * Config files are profile-scoped and cannot be attributed to individual + * plugins, so this is all-or-nothing — the UI warns about secrets before + * enabling it (the backup itself is one-to-one, never masked). + */ + includeConfig?: boolean +} + +/** + * Serialize every profile file except dependencies, lock state, and market + * cache — or, with {@link BackupOptions.includeDeps}, only the manifest with + * the selected plugins (plus, optionally, the other config files). + */ +export function createProfileBackup(profile: string, explicitDir?: string, opts?: BackupOptions): ProfileBackup { + const root = resolve(explicitDir ?? profileDir(profile)) + const manifestFile = resolve(root, 'package.json') + if (!existsSync(manifestFile)) throw new Error('profile package.json is missing') + const manifest = JSON.parse(readFileSync(manifestFile, 'utf8')) as Record + + if (opts?.includeDeps !== undefined) { + const include = new Set(opts.includeDeps) + if (include.size === 0) throw new Error('no plugins selected') + const dependencies = manifest.dependencies === null || typeof manifest.dependencies !== 'object' || Array.isArray(manifest.dependencies) + ? {} + : manifest.dependencies as Record + const filteredDeps: Record = {} + for (const [name, spec] of Object.entries(dependencies)) if (include.has(name)) filteredDeps[name] = spec + const dsh = manifest.dsh === null || typeof manifest.dsh !== 'object' || Array.isArray(manifest.dsh) + ? undefined + : manifest.dsh as { profile?: unknown } + const profileBlock = dsh?.profile === null || typeof dsh?.profile !== 'object' || Array.isArray(dsh?.profile) + ? undefined + : dsh.profile as { bundles?: unknown } + const bundles = Array.isArray(profileBlock?.bundles) ? profileBlock.bundles as unknown[] : [] + const filteredBundles = bundles.filter((name): name is string => typeof name === 'string' && include.has(name)) + if (Object.keys(filteredDeps).length === 0 && filteredBundles.length === 0) { + throw new Error('none of the selected plugins are in this profile') + } + const filteredManifest: Record = { ...manifest } + filteredManifest.dependencies = filteredDeps + if (dsh !== undefined) { + filteredManifest.dsh = { ...dsh, profile: { ...(profileBlock ?? {}), bundles: filteredBundles } } + } + const files: BackupFile[] = [{ path: 'package.json', json: filteredManifest }] + if (opts.includeConfig === true) { + for (const path of profileFiles(root).sort()) { + if (path === 'package.json') continue + files.push({ path, lines: readFileSync(resolve(root, path), 'utf8').split(/\r?\n/) }) + } + } + const partial: ProfileBackup = { format: BACKUP_FORMAT, version: 0.2, createdAt: new Date().toISOString(), profile, files } + if (Buffer.byteLength(JSON.stringify(partial)) > MAX_BACKUP_BYTES) throw new Error('profile configuration is too large to back up') + return partial + } + + const files: BackupFile[] = profileFiles(root).sort().map((path) => { + const content = readFileSync(resolve(root, path), 'utf8') + return path === 'package.json' + ? { path, json: JSON.parse(content) as Record } + : { path, lines: content.split(/\r?\n/) } + }) + if (!files.some(file => file.path === 'package.json')) throw new Error('profile package.json is missing') + const backup: ProfileBackup = { format: BACKUP_FORMAT, version: 0.2, createdAt: new Date().toISOString(), profile, files } + if (Buffer.byteLength(JSON.stringify(backup)) > MAX_BACKUP_BYTES) throw new Error('profile configuration is too large to back up') + return backup +} + +export function validatedBackup(value: unknown): ProfileBackup { + if (value === null || typeof value !== 'object') throw new Error('invalid backup') + const backup = value as Partial + if (backup.format !== BACKUP_FORMAT || backup.version !== 0.2 || !Array.isArray(backup.files)) { + throw new Error('unsupported backup format') + } + if (backup.files.length > MAX_FILES) throw new Error('invalid backup contents') + const files: BackupFile[] = [] + const paths = new Set() + for (const value of backup.files as unknown[]) { + if (value === null || typeof value !== 'object') throw new Error('invalid backup contents') + const file = value as { path?: unknown; json?: unknown; lines?: unknown } + const path = file.path + if (typeof path !== 'string') throw new Error('invalid backup contents') + if (path === '' || isAbsolute(path) || path.split(/[\\/]/).includes('..')) throw new Error(`unsafe backup path: ${path}`) + const normalized = path.replaceAll('\\', '/') + if (normalized.split('/').some(part => SKIP_NAMES.has(part))) throw new Error(`excluded backup path: ${path}`) + if (paths.has(normalized)) throw new Error(`duplicate backup path: ${path}`) + paths.add(normalized) + if (path === 'package.json') { + if (file.json === null || typeof file.json !== 'object' || Array.isArray(file.json)) throw new Error('backup package.json is invalid') + files.push({ path, json: file.json as Record }) + } else { + if (!Array.isArray(file.lines) || !file.lines.every(line => typeof line === 'string')) throw new Error(`invalid file content: ${path}`) + files.push({ path, lines: file.lines as string[] }) + } + } + if (!files.some(file => file.path === 'package.json')) throw new Error('invalid backup contents') + if (Buffer.byteLength(JSON.stringify(backup)) > MAX_BACKUP_BYTES) throw new Error('backup is too large') + return { ...backup, files } as ProfileBackup +} + +/** Atomically overwrite backed-up files and return a rollback for install failure. */ +export function restoreProfileBackup(profile: string, value: unknown, explicitDir?: string): { files: number; rollback(): void } { + const backup = validatedBackup(value) + const root = resolve(explicitDir ?? profileDir(profile)) + const previous = new Map() + mkdirSync(root, { recursive: true }) + const rollback = (): void => { + for (const [target, content] of previous) { + if (content === null) rmSync(target, { force: true }) + else writeFileSync(target, content) + } + } + try { + for (const file of backup.files) { + const { path } = file + const target = resolve(root, path) + if (!target.startsWith(root + sep)) throw new Error(`unsafe backup path: ${path}`) + ensureSafeParent(root, dirname(target), path) + if (existsSync(target) && !lstatSync(target).isFile()) throw new Error(`backup path is not a file: ${path}`) + previous.set(target, existsSync(target) ? readFileSync(target) : null) + const temp = `${target}.dsh-restore-${String(process.pid)}` + writeFileSync(temp, 'json' in file ? `${JSON.stringify(file.json, null, 2)}\n` : file.lines.join('\n'), 'utf8') + renameSync(temp, target) + } + } catch (error) { + rollback() + throw error + } + return { + files: previous.size, + rollback, + } +} + +/** Create missing parents one level at a time and refuse existing symlinks. */ +function ensureSafeParent(root: string, parent: string, backupPath: string): void { + const relativeParent = relative(root, parent) + if (relativeParent === '') return + let current = root + for (const part of relativeParent.split(sep)) { + current = resolve(current, part) + if (!existsSync(current)) { + mkdirSync(current) + continue + } + const stat = lstatSync(current) + if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error(`unsafe backup path: ${backupPath}`) + } +} + +interface PublicAddress { + address: string + family: 4 | 6 +} + +interface WebdavResponse { + status: number + body: Buffer +} + +async function webdavRequest( + url: string, + username: string, + password: string, + method: 'GET' | 'PUT' | 'MKCOL', + body?: string, +): Promise { + const parsed = new URL(url) + // SSRF hardening (review #63): https-only by default, and always refuse + // private/link-local targets — so a same-origin script cannot tunnel into + // the host network. Besides the obvious metadata surfaces (169.254.169.254), + // loopback matters because other services on this machine may expose HTTP + // APIs of their own. + if (parsed.protocol === 'http:') throw new Error('WebDAV requires an https:// URL') + if (parsed.protocol !== 'https:') throw new Error('invalid WebDAV URL') + if (parsed.username !== '' || parsed.password !== '') throw new Error('invalid WebDAV URL') + const address = await resolvePublicAddress(parsed.hostname) + const headers: Record = { host: parsed.host } + if (body !== undefined) { + headers['content-type'] = 'application/json' + headers['content-length'] = String(Buffer.byteLength(body)) + } + if (username !== '') headers.authorization = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}` + + // Connect to the address that was checked above instead of resolving the + // hostname a second time. This closes the DNS-rebinding window between the + // allow/deny decision and the actual socket connection. + return await new Promise((resolveRequest, reject) => { + const originalHostname = unbracketedHostname(parsed.hostname) + const request = httpsRequest({ + protocol: 'https:', + hostname: address.address, + family: address.family, + port: parsed.port === '' ? 443 : Number(parsed.port), + path: `${parsed.pathname}${parsed.search}`, + method, + headers, + servername: isIP(originalHostname) === 0 ? originalHostname : undefined, + signal: AbortSignal.timeout(30_000), + }, (response) => { + const chunks: Buffer[] = [] + let size = 0 + const maxBytes = method === 'GET' ? MAX_BACKUP_BYTES : 64 * 1024 + response.once('error', reject) + const declared = Number(response.headers['content-length']) + if (Number.isFinite(declared) && declared > maxBytes) { + response.destroy(new Error('WebDAV response is too large')) + return + } + response.on('data', (chunk: Buffer | string) => { + const value = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk) + size += value.byteLength + if (size > maxBytes) { + response.destroy(new Error('WebDAV response is too large')) + return + } + chunks.push(value) + }) + response.once('end', () => resolveRequest({ status: response.statusCode ?? 0, body: Buffer.concat(chunks) })) + }) + request.once('error', reject) + request.end(body) + }) +} + +/** + * Ancestor collection URLs of a WebDAV file, outermost first. + * `https://dav.example/a/b/x.json` → [`https://dav.example/a/`, `https://dav.example/a/b/`]. + * The server root itself is never included — it always exists, and some + * providers reject MKCOL on it. + */ +export function webdavParentCollections(url: string): string[] { + let parsed: URL + try { parsed = new URL(url) } catch { return [] } + const parts = parsed.pathname.split('/').filter(part => part !== '') + parts.pop() // the file itself + const collections: string[] = [] + let path = '' + for (const part of parts) { + path += `/${part}` + collections.push(`${parsed.origin}${path}/`) + } + return collections +} + +/** + * Upload the backup, creating missing parent collections first (#102). + * + * WebDAV servers do not create intermediate collections implicitly, so a PUT + * into a folder that does not exist yet fails — Jianguoyun answers 404, which + * read as "sync is broken" rather than "make the folder first". MKCOL on an + * existing collection answers 405, which is success for our purposes; any + * other failure is left to the PUT to report, since some providers restrict + * MKCOL while still accepting the upload. + */ +export async function uploadWebdav(url: string, username: string, password: string, backup: ProfileBackup): Promise { + for (const collection of webdavParentCollections(url)) { + try { + await webdavRequest(collection, username, password, 'MKCOL') + } catch { /* fall through: the PUT below reports the real problem */ } + } + const response = await webdavRequest(url, username, password, 'PUT', JSON.stringify(backup)) + if (response.status < 200 || response.status >= 300) { + throw new Error(response.status === 404 + ? `WebDAV upload failed: HTTP 404 — the target folder does not exist and could not be created. Some providers (e.g. Jianguoyun) refuse files at the root: use a path inside a folder, e.g. https://dav.example.com/dsh/backup.json / 目标目录不存在且无法自动创建;部分服务商(如坚果云)不允许在根目录放文件,请使用形如 https://dav.example.com/dsh/backup.json 的子目录路径` + : `WebDAV upload failed: HTTP ${response.status}`) + } +} + +/** Refuse non-global IPv4 targets, including metadata and carrier NAT ranges. */ +export function isPublicIpv4(ip: string): boolean { + const octets = ip.split('.').map(Number) + if (octets.length !== 4 || octets.some(part => !Number.isInteger(part) || part < 0 || part > 255)) return false + const [a, b] = octets + if (a === 0 || a === 10 || a === 127 || a >= 224) return false + if (a === 100 && b >= 64 && b <= 127) return false + if (a === 169 && b === 254) return false + if (a === 172 && b >= 16 && b <= 31) return false + if (a === 192 && (b === 0 || b === 168)) return false + if (a === 198 && (b === 18 || b === 19)) return false + return true +} + +/** Only public internet target hostnames are reachable for WebDAV. */ +export function isPublicHostname(hostname: string): boolean { + const lower = unbracketedHostname(hostname).toLowerCase() + const bare = lower.endsWith('.') ? lower.slice(0, -1) : lower + return bare !== '' && bare !== 'localhost' && bare !== 'metadata.google.internal' + && !bare.endsWith('.localhost') && !bare.endsWith('.internal') && !bare.endsWith('.local') +} + +/** + * Whether a WebDAV hostname may be fetched: public https targets only. + * Exported for tests. + */ +export function isPublicTarget(hostname: string): boolean { + const bare = unbracketedHostname(hostname) + const family = isIP(bare) + if (family === 4) return isPublicIpv4(bare) + if (family === 6) return isPublicIpv6(bare) + return isPublicHostname(bare) +} + +/** Only global-unicast IPv6 is usable for a server-side WebDAV connection. */ +export function isPublicIpv6(ip: string): boolean { + const bare = unbracketedHostname(ip) + if (isIP(bare) !== 6) return false + const first = Number.parseInt(bare.split(':', 1)[0] || '0', 16) + return Number.isFinite(first) && first >= 0x2000 && first <= 0x3fff +} + +function unbracketedHostname(hostname: string): string { + return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname +} + +/** Resolve once, reject every unsafe answer, and return the address to pin. */ +export async function resolvePublicAddress(hostname: string): Promise { + const bare = unbracketedHostname(hostname) + const family = isIP(bare) + if (family === 4 || family === 6) { + if (!isPublicTarget(bare)) throw new Error('invalid WebDAV URL') + return { address: bare, family } + } + if (!isPublicHostname(bare)) throw new Error('invalid WebDAV URL') + const addresses = await lookup(bare, { all: true, verbatim: true }) + if (addresses.length === 0 || addresses.some(({ address }) => !isPublicTarget(address))) { + throw new Error('invalid WebDAV URL') + } + const selected = addresses[0] + if (selected.family !== 4 && selected.family !== 6) throw new Error('invalid WebDAV URL') + return { address: selected.address, family: selected.family } +} + +export async function downloadWebdav(url: string, username: string, password: string): Promise { + const response = await webdavRequest(url, username, password, 'GET') + if (response.status < 200 || response.status >= 300) { + throw new Error(response.status === 404 + ? 'WebDAV download failed: HTTP 404 — no backup at that path yet. Upload one first, and check the URL points at the backup FILE (…/dsh/backup.json), not its folder / 该路径下还没有备份文件。请先执行一次上传,并确认地址指向备份文件本身(…/dsh/backup.json)而不是目录' + : `WebDAV download failed: HTTP ${response.status}`) + } + // Validate strictly server-side so the fetch result is never a generic + // echo of an internal response: restore only accepts real backups. + const body = JSON.parse(response.body.toString('utf8')) as unknown + validatedBackup(body) + return body +} + +export interface PluginSelection { + deps: Record + bundles: string[] +} + +/** + * The selected plugins' dependency specs and bundle entries from a backup's + * manifest. Only string specs survive — everything else in the manifest is + * untrusted and ignored (partial restore touches nothing but these). + */ +export function extractPluginSelection(backup: ProfileBackup, includeDeps: string[]): PluginSelection { + const manifest = backup.files.find(file => file.path === 'package.json' && 'json' in file) + if (manifest === undefined || !('json' in manifest)) throw new Error('backup has no package.json') + const include = new Set(includeDeps) + const json = manifest.json + const dependencies = json.dependencies === null || typeof json.dependencies !== 'object' || Array.isArray(json.dependencies) + ? {} + : json.dependencies as Record + const deps: Record = {} + for (const [name, spec] of Object.entries(dependencies)) { + if (typeof spec === 'string' && include.has(name)) deps[name] = spec + } + const dsh = json.dsh === null || typeof json.dsh !== 'object' || Array.isArray(json.dsh) + ? undefined + : json.dsh as { profile?: unknown } + const profileBlock = dsh?.profile === null || typeof dsh?.profile !== 'object' || Array.isArray(dsh?.profile) + ? undefined + : dsh.profile as { bundles?: unknown } + const bundles = Array.isArray(profileBlock?.bundles) ? profileBlock.bundles as unknown[] : [] + return { + deps, + bundles: bundles.filter((name): name is string => typeof name === 'string' && include.has(name)), + } +} + +/** + * Merge a backup's manifest into the profile's current manifest so a restore + * never deletes plugins the target machine already has: current deps stay, + * backup specs win on name conflicts; bundle lists are unioned. When + * `selection` is given, only the selected plugins are merged in. + */ +/** + * Dependencies whose spec points at an absolute local path — `link:/Users/…` + * or `file:/home/…` (#205 by @Rudyy898). + * + * These are perfectly valid on the machine that wrote them and meaningless + * anywhere else, so a backup carrying one restores a manifest that `pnpm + * install` cannot satisfy: the path does not exist on the new machine and + * the whole restore fails on it. + * + * Reported, NOT rewritten. Turning `link:/Users/me/dev/plugin` into + * something portable means deciding where those files should live and + * whether to carry them at all, which is a design question and not + * something a restore should answer on the user's behalf. Naming them lets + * the operator decide before the install runs — which is the part that was + * missing. + * + * Relative `file:./vendor/x` specs are left alone: they resolve against the + * profile directory, which the restore recreates, so they travel fine. + */ +export function unportableDeps(dependencies: unknown): Array<{ name: string; spec: string }> { + if (dependencies === null || typeof dependencies !== 'object' || Array.isArray(dependencies)) return [] + const found: Array<{ name: string; spec: string }> = [] + for (const [name, raw] of Object.entries(dependencies as Record)) { + if (typeof raw !== 'string') continue + const match = /^(?:link|file):(.+)$/i.exec(raw) + if (match === null) continue + let path = match[1] + try { path = decodeURIComponent(path) } catch { /* keep the literal spec */ } + // POSIX absolute, Windows drive-letter, or UNC — every shape that names + // a location outside this profile. + if (/^\//.test(path) || /^[A-Za-z]:[\\/]/.test(path) || /^\\\\/.test(path)) found.push({ name, spec: raw }) + } + return found +} + +export function mergeRestoreManifest( + backupManifest: Record, + current: Record, + selection?: PluginSelection, +): Record { + const merged: Record = { ...backupManifest } + const backupDeps = backupManifest.dependencies === null || typeof backupManifest.dependencies !== 'object' || Array.isArray(backupManifest.dependencies) + ? {} + : backupManifest.dependencies as Record + const backupBundles = Array.isArray((backupManifest.dsh as { profile?: { bundles?: unknown } } | undefined)?.profile?.bundles) + ? ((backupManifest.dsh as { profile: { bundles: unknown[] } }).profile.bundles) + : [] + const currentDeps = current.dependencies === null || typeof current.dependencies !== 'object' || Array.isArray(current.dependencies) + ? {} + : current.dependencies as Record + const currentBundles = Array.isArray((current.dsh as { profile?: { bundles?: unknown } } | undefined)?.profile?.bundles) + ? ((current.dsh as { profile: { bundles: unknown[] } }).profile.bundles) + : [] + + // Deps: keep the target's, overlay the backup's (or only the selection). + const deps: Record = { ...currentDeps } + const sourceDeps = selection !== undefined ? selection.deps : backupDeps + for (const [name, spec] of Object.entries(sourceDeps)) deps[name] = spec + merged.dependencies = deps + + // Bundles: union of target and backup (or selection), de-duplicated. + const bundles = new Set() + for (const name of currentBundles) if (typeof name === 'string') bundles.add(name) + const sourceBundles = selection !== undefined ? selection.bundles : backupBundles + for (const name of sourceBundles) if (typeof name === 'string') bundles.add(name) + + const currentDsh = current.dsh === null || typeof current.dsh !== 'object' || Array.isArray(current.dsh) + ? undefined + : current.dsh as { profile?: unknown } + const currentProfile = currentDsh?.profile === null || typeof currentDsh?.profile !== 'object' || Array.isArray(currentDsh?.profile) + ? undefined + : currentDsh.profile as Record + const backupDsh = merged.dsh === null || typeof merged.dsh !== 'object' || Array.isArray(merged.dsh) + ? undefined + : merged.dsh as { profile?: unknown } + const backupProfile = backupDsh?.profile === null || typeof backupDsh?.profile !== 'object' || Array.isArray(backupDsh?.profile) + ? undefined + : backupDsh.profile as Record + + const profileMerged: Record = { ...(backupProfile ?? {}), ...(currentProfile ?? {}), bundles: [...bundles] } + const dshMerged: Record = { ...(backupDsh ?? {}), ...(currentDsh ?? {}), profile: profileMerged } + merged.dsh = dshMerged + return merged +} diff --git a/packages/dshmarket/src/catalog-npm.ts b/packages/dshmarket/src/catalog-npm.ts new file mode 100644 index 000000000..c9340a66d --- /dev/null +++ b/packages/dshmarket/src/catalog-npm.ts @@ -0,0 +1,120 @@ +/** + * Reading the plugin catalog out of a published npm package. + * + * The catalog's own host is the thing being worked around. `plugins.json` is + * served from GitHub Pages, and the public GitHub proxies that make GitHub + * usable from mainland China refuse any hostname that is not github.com's + * own — measured: the project domain comes back 403. So the file cannot be + * carried through the same route as everything else while it lives there. + * + * Published to npm it can be. Every China npm mirror already carries it, it + * is the same infrastructure the plugins themselves arrive on, and it needs + * no service that did not already have to work. It also gains a version + * number, which the origin never had: a catalog build that ships bad data + * can be rolled back instead of only being fixed forwards. + * + * Mirrors rewrite `dist.tarball` to point at themselves, so following that + * field is what keeps the download on the mirror rather than bouncing back + * to the origin registry — verified against the Tencent mirror, which serves + * its own host in that field. + */ + +import { gunzipSync } from 'node:zlib' +import { marketFetch } from './net.ts' + +/** Long enough for a multi-megabyte tarball on a slow link, short enough to fall back. */ +const TIMEOUT_MS = 20_000 + +/** + * A tar entry header, as far as this needs to read one. + * + * Written out rather than pulled from a dependency: the format is 512-byte + * headers with the name at offset 0 and an octal size at 124, and a reader + * for one known filename is shorter than the argument for adding a package + * to a plugin's runtime. + */ +const NAME_OFFSET = 0 +const NAME_LENGTH = 100 +const SIZE_OFFSET = 124 +const SIZE_LENGTH = 12 +const TYPE_OFFSET = 156 +const BLOCK = 512 + +/** + * One file's bytes from a gzipped tar, or null when it is not in there. + * + * @param gz - the gzipped tarball. + * @param wanted - the exact entry name, npm-style (`package/plugins.json`). + */ +export function fileFromTarball(gz: Buffer, wanted: string): Buffer | null { + const buf = gunzipSync(gz) + let offset = 0 + while (offset + BLOCK <= buf.length) { + const name = buf.toString('utf8', offset + NAME_OFFSET, offset + NAME_OFFSET + NAME_LENGTH).replace(/\0.*$/s, '') + // Two consecutive empty headers end a tar; one is enough to stop here. + if (name === '') break + const rawSize = buf.toString('ascii', offset + SIZE_OFFSET, offset + SIZE_OFFSET + SIZE_LENGTH).replace(/\0.*$/s, '').trim() + const size = Number.parseInt(rawSize, 8) + if (!Number.isFinite(size) || size < 0) break + const type = String.fromCharCode(buf[offset + TYPE_OFFSET] ?? 0) + offset += BLOCK + // '0' and NUL both mean a regular file; anything else (directories, + // links, pax headers) is skipped rather than mistaken for content. + if ((type === '0' || type === '\0') && name === wanted) { + return buf.subarray(offset, offset + size) + } + offset += Math.ceil(size / BLOCK) * BLOCK + } + return null +} + +/** What a registry says about a package's newest release. */ +interface Packument { + version?: unknown + dist?: { tarball?: unknown } +} + +/** + * The catalog carried by a published package, and the version it came from. + * + * The version doubles as the cache validator — a better one than an ETag, + * because it is meaningful to a human reading a log and it is the thing a + * rollback would change. + * + * @param registry - registry base, no trailing slash. + * @param pkg - package name. + * @param known - a version already held, to skip re-downloading. + * @returns the parsed JSON and its version, or `{ version, data: null }` when + * the published version is the one already held. + * @throws when the package, the tarball or the file inside it cannot be read. + */ +export async function catalogFromPackage( + registry: string, + pkg: string, + known?: string, + /** The entry to extract; the catalog package carries `plugins.json`, companions theirs. */ + file = 'package/plugins.json', +): Promise<{ version: string; data: unknown | null }> { + const metaRes = await marketFetch(`${registry}/${encodeURIComponent(pkg)}/latest`, { + signal: AbortSignal.timeout(TIMEOUT_MS), + headers: { accept: 'application/json', 'user-agent': 'dsh-market' }, + }) + if (!metaRes.ok) throw new Error(`HTTP ${String(metaRes.status)} reading ${pkg} metadata`) + const meta = (await metaRes.json()) as Packument + const version = typeof meta.version === 'string' ? meta.version : null + const tarball = typeof meta.dist?.tarball === 'string' ? meta.dist.tarball : null + if (version === null || tarball === null) throw new Error(`${pkg} metadata names no version or tarball`) + // Nothing changed, so nothing to download. The whole point of putting the + // catalog on a mirror is the bytes it saves; re-fetching a package we + // already hold would give most of them back. + if (known !== undefined && known === version) return { version, data: null } + + // Follow `dist.tarball` rather than composing a URL. A mirror rewrites + // this field to its own host, and composing one would send the download + // back to the origin registry the region exists to avoid. + const tarRes = await marketFetch(tarball, { signal: AbortSignal.timeout(TIMEOUT_MS) }) + if (!tarRes.ok) throw new Error(`HTTP ${String(tarRes.status)} reading ${pkg} tarball`) + const bytes = fileFromTarball(Buffer.from(await tarRes.arrayBuffer()), file) + if (bytes === null) throw new Error(`${pkg}@${version} carries no ${file}`) + return { version, data: JSON.parse(bytes.toString('utf8')) as unknown } +} diff --git a/packages/dshmarket/src/changelog.ts b/packages/dshmarket/src/changelog.ts new file mode 100644 index 000000000..e4504400f --- /dev/null +++ b/packages/dshmarket/src/changelog.ts @@ -0,0 +1,249 @@ +/** + * Update notes: what changed between an installed version and HEAD. + * + * Every byte of GitHub data comes from the catalog side's daily probe, + * published as the `dsh-plugin-updates` package (origin fallback + * `updates.json` beside `plugins.json`). The market asks GitHub for nothing: + * anonymous REST quota is shared per egress IP and already unreliable behind + * common proxies, and this data is wanted by thousands of markets but + * produced perfectly well once a day by one probe. + * + * What the catalog cannot know is where each user's installed version sits, + * so slicing the commit tail at that boundary happens here: the same sha the + * update check resolved (`current`) marks where "new" begins. A tail that + * does not contain it means the range is wider than the tail — those are + * labelled recent commits rather than pretending to be an exact interval. + * Plugins with neither notes nor commits fall back to npm publish times, and + * a plugin nothing answers for gets a neutral "no notes" — absence of release + * notes is the ordinary condition of small plugins, not a defect. + */ + +import { fileFromTarball } from './catalog-npm.ts' +import { marketFetch } from './net.ts' +import { activeRegion, routesFor } from './regions.ts' +import { profileDir, readInstalled } from './profile.ts' +import { repoOfTarget } from './sources.ts' +import { checkUpdates } from './updates.ts' + +const UPDATES_PACKAGE = 'dsh-plugin-updates' +const UPDATES_FILE = 'package/updates.json' +/** The origin copy sits beside plugins.json on the catalog host. */ +const ORIGIN_UPDATES = process.env.DSHM_UPDATES_ORIGIN ?? 'https://awesome-dsh-plugin.com/updates.json' +/** Notes are produced daily; re-checking hourly bounds staleness without churn. */ +const NOTES_TTL_MS = 60 * 60 * 1000 +const TIMEOUT_MS = 15_000 + +export interface ReleaseNotes { + tag: string | null + name: string | null + publishedAt: string | null + url: string | null + body: string +} + +export interface CommitNote { + sha: string + message: string + date: string | null +} + +interface UpdatesEntry { + release?: ReleaseNotes | null + commits?: CommitNote[] +} + +interface UpdatesPayload { + count?: number + updates?: Record +} + +let notesCache: { at: number; data: UpdatesPayload } | null = null + +/** + * The update-notes payload from whichever route the region serves. + * + * Same source order as the catalog itself — the npm package first where one + * exists (its mirror reach is the reason the package exists), then the + * origin. Version-keyed like the catalog too: an unchanged payload costs one + * packument read per TTL, not a tarball. + */ +export async function loadUpdateNotes(force = false): Promise { + if (!force && notesCache !== null && Date.now() - notesCache.at < NOTES_TTL_MS) { + return notesCache.data + } + const routes = routesFor(activeRegion()) + const attempt = async (): Promise => { + // The npm package, on the region's registry — mirrors rewrite dist.tarball + // to themselves, so following that field keeps the download local. + const metaRes = await marketFetch(`${routes.npmRegistry}/${encodeURIComponent(UPDATES_PACKAGE)}/latest`, { + signal: AbortSignal.timeout(TIMEOUT_MS), + headers: { accept: 'application/json', 'user-agent': 'dsh-market' }, + }) + if (!metaRes.ok) throw new Error(`HTTP ${String(metaRes.status)} reading ${UPDATES_PACKAGE} metadata`) + const meta = await metaRes.json() as { dist?: { tarball?: unknown } } + const tarball = typeof meta.dist?.tarball === 'string' ? meta.dist.tarball : null + if (tarball === null) throw new Error(`${UPDATES_PACKAGE} names no tarball`) + const tarRes = await marketFetch(tarball, { signal: AbortSignal.timeout(TIMEOUT_MS) }) + if (!tarRes.ok) throw new Error(`HTTP ${String(tarRes.status)} reading ${UPDATES_PACKAGE} tarball`) + const bytes = fileFromTarball(Buffer.from(await tarRes.arrayBuffer()), UPDATES_FILE) + if (bytes === null) throw new Error(`${UPDATES_PACKAGE} carries no ${UPDATES_FILE}`) + return JSON.parse(bytes.toString('utf8')) as UpdatesPayload + } + const attemptOrigin = async (): Promise => { + const res = await marketFetch(ORIGIN_UPDATES, { + signal: AbortSignal.timeout(TIMEOUT_MS), + headers: { accept: 'application/json', 'user-agent': 'dsh-market' }, + }) + if (!res.ok) throw new Error(`HTTP ${String(res.status)} reading ${ORIGIN_UPDATES}`) + return await res.json() as UpdatesPayload + } + // Two independent sources, each tried twice — the same retry economics as + // the catalog loader, for the same transient-loss reasons. + let lastError: unknown = null + for (const attemptFn of [attempt, attemptOrigin]) { + for (let i = 0; i < 2; i++) { + try { + const data = await attemptFn() + notesCache = { at: Date.now(), data } + return data + } catch (error) { + lastError = error + } + } + } + throw lastError instanceof Error ? lastError : new Error('update notes unavailable') +} + +/** Drop the cached payload (nothing currently invalidates it mid-process). */ +export function invalidateUpdateNotes(): void { + notesCache = null +} + +/** + * Slice the commit tail at the installed sha. + * + * @returns the commits newer than `installed`, and whether the boundary was + * actually found — `found === false` means the range is wider than the tail + * (or history diverged), so callers must label the result "recent" rather + * than exact. + */ +export function sliceCommitsAt(items: CommitNote[], installed: string | null): { items: CommitNote[]; found: boolean } { + if (installed === null) return { items, found: false } + const out: CommitNote[] = [] + for (const commit of items) { + if (commit.sha === installed) return { items: out, found: true } + out.push(commit) + } + return { items: out, found: false } +} + +/** The repo url key an entry's update notes live under, or null. */ +export function repoKeyOf(spec: string): string | null { + const repo = repoOfTarget(spec)?.split('#')[0] ?? null + return repo === null ? null : `https://github.com/${repo}` +} + +/** + * Look an entry up by its catalog url. + * + * The catalog keys entries by the url as listed (author's casing preserved); + * the market resolves repos to lowercase (as every other consumer of + * `repoOfTarget` sees them). Match case-insensitively so the two sides never + * disagree over a capital letter. + */ +export function entryForRepo(payload: UpdatesPayload, key: string): UpdatesEntry | undefined { + const direct = payload.updates?.[key] + if (direct !== undefined) return direct + const lower = key.toLowerCase() + for (const [k, v] of Object.entries(payload.updates ?? {})) { + if (k.toLowerCase() === lower) return v + } + return undefined +} + +/** + * Publish times for an npm package's recent versions, newest first. + * + * The last resort before "no notes": authors who ship neither releases nor + * informative commits still usually tag versions, and "0.4.0 landed three + * days ago" beats nothing. Reads the full packument once and caches it — + * the doc is heavy, and a user may open the dialog twice. + */ +const timesCache = new Map() +const TIMES_TTL_MS = 30 * 60 * 1000 + +export async function npmPublishTimes(name: string): Promise<{ version: string; date: string }[]> { + const hit = timesCache.get(name) + if (hit !== undefined && Date.now() - hit.at < TIMES_TTL_MS) return hit.data + const registry = routesFor(activeRegion()).npmRegistry + const res = await marketFetch(`${registry}/${encodeURIComponent(name)}`, { + signal: AbortSignal.timeout(TIMEOUT_MS), + headers: { accept: 'application/json', 'user-agent': 'dsh-market' }, + }) + if (!res.ok) throw new Error(`HTTP ${String(res.status)} reading ${name}`) + const doc = await res.json() as { time?: Record } + const time = doc.time ?? {} + const data = Object.entries(time) + .filter(([version]) => version !== 'created' && version !== 'modified') + .sort(([, a], [, b]) => Date.parse(b) - Date.parse(a)) + .slice(0, 5) + .map(([version, date]) => ({ version, date })) + timesCache.set(name, { at: Date.now(), data }) + return data +} + +/** Everything the dialog renders for one plugin, already resolved. */ +export interface UpdateNotes { + kind: 'release' | 'commits' | 'npm' | 'none' + /** Present for `release`: the author's own notes for the latest release. */ + release?: ReleaseNotes + /** Present for `commits`: the tail, sliced when the boundary was found. */ + commits?: { items: CommitNote[]; found: boolean } + /** Present for `npm`: recent versions with their publish dates. */ + npmTimes?: { version: string; date: string }[] +} + +/** + * Resolve the notes for one installed plugin, or `{ kind: 'none' }`. + * + * Never throws: every failure along the way degrades to the next tier, the + * same contract the issue promised — a dialog that cannot load its data shows + * a neutral statement, not an error. + */ +export async function updateNotesFor( + profile: string, + explicitDir: string | undefined, + name: string, +): Promise { + const activeProfileDir = profileDir(profile, explicitDir) + const spec = readInstalled(profile, activeProfileDir)[name] + if (spec === undefined || spec.startsWith('link:') || spec.startsWith('file:')) { + return { kind: 'none' } + } + try { + const payload = await loadUpdateNotes() + const key = repoKeyOf(spec) + const entry = key === null ? undefined : entryForRepo(payload, key) + // Both tiers below need the installed sha for github-kind installs; it + // comes from the update check the row was rendered from, cache-warm. + const statuses = await checkUpdates(profile, false, explicitDir).catch(() => null) + const current = statuses?.[name]?.current ?? null + + if (entry?.release !== undefined && entry.release !== null && (entry.release.body !== '' || entry.release.tag !== null)) { + return { kind: 'release', release: entry.release } + } + if (entry?.commits !== undefined && entry.commits.length > 0) { + return { kind: 'commits', commits: sliceCommitsAt(entry.commits, current) } + } + if (key === null) { + // Not a github-sourced plugin at all: npm times are the only tier left. + return { kind: 'npm', npmTimes: await npmPublishTimes(name) } + } + // A github plugin whose repo answered nothing — releases 404 AND the log + // came back empty. Rare enough to just say so rather than ask npm about a + // name that may not even exist there. + return { kind: 'none' } + } catch { + return { kind: 'none' } + } +} diff --git a/packages/dshmarket/src/channels.ts b/packages/dshmarket/src/channels.ts new file mode 100644 index 000000000..57d28b946 --- /dev/null +++ b/packages/dshmarket/src/channels.ts @@ -0,0 +1,70 @@ +/** + * The market's release channels: which build of ITSELF it offers. + * + * Only the market follows this. Other plugins are never pulled from a + * prerelease on the strength of a setting the user made about the market — + * opting into early builds is volunteering to try THIS plugin early, not to + * be handed every other author's unreleased work. + * + * The model lives in its own module because it is the part with rules + * rather than plumbing: three channels, one of them hidden, a mapping to + * npm dist-tags, and a resolution order that has already been got wrong + * once (see `resolveChannel`). + */ + +/** A channel the market can follow. */ +export type Channel = 'stable' | 'beta' | 'dev' + +/** + * The npm dist-tag each channel installs from. + * + * `dev` is published straight from a branch with no git tag behind it, so a + * version carries a timestamp and a short SHA (`1.15.0-dev.20260818-3f1432e`) + * and is never reused. That is what makes a dev build disposable: nothing in + * the repository's history refers to it. + */ +export const DIST_TAG: Record = { + stable: 'latest', + beta: 'beta', + dev: 'dev', +} + +/** + * Every channel a user may pick. All three, always. + * + * `dev` was behind a developer-mode switch for one version, on the reasoning + * that a build published straight off a branch should not sit beside + * "stable" looking like a third degree of caution. The switch cost more than + * it bought: a stored mode, a route to change it, a rule for what happens to + * a dev choice when it is turned off, and a control whose own purpose needed + * explaining. A plainly labelled option a user can read is simpler than a + * hidden one plus the machinery that hides it — the label does the work the + * gate was doing. + */ +export const CHANNELS: readonly Channel[] = ['stable', 'beta', 'dev'] + +/** Narrow an untrusted value to a Channel, or null. */ +export function asChannel(value: unknown): Channel | null { + return value === 'stable' || value === 'beta' || value === 'dev' ? value : null +} + +/** + * Which channel applies right now. + * + * A choice on record always wins — including "stable" while a prerelease is + * running, which is the only way back off a channel. Only the ABSENCE of a + * choice is derived, and then from what is actually running: installing + * `dshmarket@beta` by hand IS the subscription, and treating that as + * "stable" costs updates rather than just clarity — on the stable channel + * `latest` (1.13.1) is not newer than an installed 1.14.0-beta.1, so the + * market answers "up to date" and the next beta is never offered. + * + * Which makes `undefined` load-bearing: it has to survive both the settings + * schema (no `.default`) and state.json (field omitted) or "never chose" + * silently becomes "chose stable". + * + */ +export function resolveChannel(setting: Channel | undefined, version: string): Channel { + if (setting !== undefined) return setting + return version.includes('-') ? 'beta' : 'stable' +} diff --git a/packages/dshmarket/src/check.ts b/packages/dshmarket/src/check.ts new file mode 100644 index 000000000..62f451fc5 --- /dev/null +++ b/packages/dshmarket/src/check.ts @@ -0,0 +1,1232 @@ +/** + * Profile composition diagnostics — issue #98 (phase 1): the check-only + * "plugin loading layer and conflict view". + * + * Pure filesystem analysis of one dsh profile directory; no processes, no + * network, no writes. It answers, for the profile the market is serving: + * + * 1. What is the actual bundle stack (dsh.profile.bundles order) and where + * does each layer come from (official in-box bundle vs community, the + * dependency spec, the resolved directory)? + * 2. Which loader entry ids does the composed tree contain, and are any + * duplicated across layers (the "duplicate loader entry id" boot failure + * from #98)? Which rows does a later layer override? + * 3. Does any installed plugin pull a DSH host core package + * (@deepseek-ai/dsh, @deepseek-ai/dsh-tools, @deepseek-ai/cordis, …) in + * as an ordinary dependency — the dsh-excel-chat failure mode where the + * plugin's copy gets hoisted to the profile root and shadows the host's + * version (tool calls die, minimal preset fails to mount)? + * 4. Are there multiple versions of one core package in the lockfile, and + * do plugin peerDependencies ranges match the resolved core version? + * 5. Do effective user/home patch entries reference npm package roots that + * are installed in the profile-visible node_modules ancestry? + * + * The composition step mirrors @deepseek-ai/dsh-app-boot's applyEntryPatches + * (same js-yaml dialect incl. `!!js` scalars), so the rows reported here are + * what actually mounts at boot. + */ + +import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs' +import { createRequire, isBuiltin } from 'node:module' +import { dirname, isAbsolute, join, resolve } from 'node:path' +import { JSON_SCHEMA, Type, load } from 'js-yaml' +import { findDshInstallDir } from './dsh-install.ts' +import { resolveDshHome } from './home-paths.ts' +import { INBOX_BUNDLES, readBundleRules, suggestOrder, validateOrder } from './order.ts' + +export { findDshInstallDir } from './dsh-install.ts' + +/** js-yaml dialect for `!!js` scalars — identical to dsh-app-boot's entryListSchema. */ +const jsExpr = new Type('tag:yaml.org,2002:js', { + kind: 'scalar', + resolve: (data: unknown): boolean => typeof data === 'string', + construct: (data: unknown): unknown => ({ __jsExpr: String(data) }), +}) +const entrySchema = JSON_SCHEMA.extend(jsExpr) + +/** Boot-breaking or confirmed problems vs informational warnings. */ +export interface CheckSummary { + ok: boolean + errors: string[] + warnings: string[] +} + +/** One layer of the bundle stack, in `dsh.profile.bundles` order. */ +export interface BundleLayer { + name: string + /** Dependency spec from the profile package.json (npm range, git, link:…). */ + source: string + /** 'official' = in-box dsh bundle; 'community' = everything else. */ + kind: 'official' | 'community' + /** Resolved package directory; null when the package is not installed. */ + directory: string | null + /** Absolute path of the layer's patch file; null when undeclared/missing. */ + patchPath: string | null + /** + * An in-box bundle whose directory could not be located — a gap in what + * this process can see, not a defect in the profile (#369). Distinct from + * `error`, which asserts the profile will not boot. + */ + unresolvedInbox?: boolean + /** Why this layer cannot load at boot (missing dir / no dsh.bundle / …). */ + error: string | null + /** Loader entry ids this bundle's patch inserts. */ + entries: string[] + /** The patch file exists but could not be parsed as the entry-list dialect. */ + parseError: string | null + /** Author-declared ordering constraints (issue #98 phase 2), when present. */ + order?: { + before?: string[] + after?: string[] + /** Violated rules for THIS bundle in the current stack order. */ + conflicts?: Array<{ name: string; reason: string }> + } +} + +/** One loader row of the composed tree, with the layer that introduced it. */ +export interface LoaderRow { + id: string + /** Bundle package name, 'user-patch' (profile cordis.patch.yml) or 'home-patch'. */ + layer: string + kind: 'insert' | 'patch' + name?: string +} + +/** An id present in more than one composed row — the #98 duplicate-id boot failure. */ +export interface DuplicateId { + id: string + /** Every layer that inserts/defines this id. */ + layers: string[] + count: number +} + +/** A non-insert patch row that merged into an existing entry (later layer wins). */ +export interface OverrideRow { + id: string + layer: string + /** Layers that introduced the targeted entry earlier in the stack. */ + overriddenLayers: string[] +} + +/** A patch row that matched nothing at boot (dsh warns and skips it). */ +export interface OrphanRow { + id: string + layer: string + reason: string +} + +/** Directional verdict for one confirmed peer mismatch (issue #201 diagnostics). */ +export interface PeerRisk { + plugin: string + peer: string + range: string + resolved: string + direction: 'belowMin' | 'aboveMax' +} + +export interface PeerWarning { + plugin: string + peer: string + range: string + resolved: string + reason: 'aboveMax' | 'optional' +} + +/** classifyPeer result: risk / warning / none — none means informational. */ +export type PeerVerdict = + | { kind: 'risk'; risk: PeerRisk } + | { kind: 'warning'; warning: PeerWarning } + | { kind: 'none' } + +/** A plugin peerDependencies range vs the resolved version. */ +export interface PeerMismatch { + plugin: string + name: string + range: string + resolved: string | null + /** False = confirmed incompatible; null = could not be evaluated. */ + satisfied: boolean | null + /** + * The declaring plugin marked this peer `optional` in + * `peerDependenciesMeta`. Carried so the summary can hold the same line + * `classifyPeer` already does: an optional peer that does not match is + * the plugin saying "I work without this", not a broken install (#275). + */ + optional?: boolean + /** + * The directional verdict, attached by `/dsh-market/check` for rows that + * did not match. It is computed on the server because the client cannot + * read `peerDependenciesMeta` off disk, and the tiering would otherwise + * have to be guessed from the range string (#201). + */ + verdict?: PeerVerdict +} + +/** + * Loader entries sharing one NAME across DIFFERENT layers — the Loader + * registers plugins by name, so a later layer's row with the same name + * shadows the earlier one at runtime. Same-layer rows sharing a name are + * routine (a bundle defining several entries under one name) and are never + * reported here. Unlike duplicate ids (report.duplicates), which fail the + * boot outright, shadowing names only decide which entry wins at runtime. + */ +export interface DuplicateName { + name: string + /** Every layer that inserts/defines a row with this name. */ + layers: string[] + count: number +} + +/** Distinct resolved versions of one core package found in the lockfile. */ +export interface MultiVersion { + name: string + versions: string[] + /** Version hoisted at the profile root, when present. */ + hoisted: string | null +} + +/** The full check report for one profile. */ +export interface CheckReport { + profile: string + scannedAt: number + bundles: BundleLayer[] + rows: LoaderRow[] + duplicates: DuplicateId[] + duplicateNames: DuplicateName[] + overrides: OverrideRow[] + orphans: OrphanRow[] + peerMismatches: PeerMismatch[] + multiVersion: MultiVersion[] + /** Before/after rule conflicts in the CURRENT bundle order (issue #98 phase 2). */ + orderConflicts: Array<{ name: string; reason: string }> + /** LOOT-style auto-fix: a community order satisfying every declared rule. */ + suggestedOrder: { ok: true; order: string[] } | { ok: false; cycle: string[] } | null + summary: CheckSummary +} + +export interface CheckOptions { + /** DSH host install dir; auto-detected from the CLI entry or Desktop resources when omitted. */ + dshInstallDir?: string + /** Harness home for the home-level patch layer; defaults to $DSH_HOME or ~/.dsh. */ + homeDir?: string +} + +function isRecord(value: unknown): value is Record { + return value !== null && typeof value === 'object' && !Array.isArray(value) +} + +/** Parse entry-list source with the DSH dialect; null when it is not a list. */ +export function parsePatchText(text: string): unknown[] | null { + try { + const value = load(text, { schema: entrySchema }) + return Array.isArray(value) ? value : null + } catch { + return null + } +} + +/** Parse one entry-list patch file with the DSH dialect; null when unreadable. */ +export function parsePatchFile(path: string): unknown[] | null { + try { + return parsePatchText(readFileSync(path, 'utf8')) + } catch { + return null + } +} + +/** Whether `name` goes through the Loader's bare-module resolver. */ +function isBareLoaderSpecifier(name: string): boolean { + return !name.startsWith('.') + && !name.startsWith('#') + && !isAbsolute(name) + && !/^[a-z][a-z\d+.-]*:/i.test(name) +} + +/** Npm package root owning one bare Loader specifier. */ +function packageRoot(specifier: string): string | null { + const parts = specifier.split('/') + const segments = specifier.startsWith('@') ? parts.slice(0, 2) : parts.slice(0, 1) + if (segments.length !== (specifier.startsWith('@') ? 2 : 1)) return null + if (segments.some(segment => segment === '' || segment === '.' || segment === '..' + || segment.includes('%') || segment.includes('\\'))) return null + if (specifier.startsWith('@') && (segments[0]?.length ?? 0) <= 1) return null + return segments.join('/') +} + +/** + * Package-root presence from the Loader-visible profile ancestry only. + * An explicit walk avoids CommonJS global lookup directories that + * `createRequire(...).resolve.paths()` appends but Node ESM does not search. + */ +function profilePackageInstalled(profileDirectory: string, name: string): boolean { + try { + const profile = JSON.parse(readFileSync(join(profileDirectory, 'package.json'), 'utf8')) as unknown + if (isRecord(profile) && profile.name === name + && profile.exports !== undefined && profile.exports !== null) return true + } catch { /* not a self-referencing profile package */ } + let directory = resolve(profileDirectory) + while (true) { + const packageDirectory = join(directory, 'node_modules', name) + // Node stops at the nearest matching package directory. A partial install + // there shadows any healthy parent copy and must not be accepted. + try { + if (statSync(packageDirectory).isDirectory()) { + return existsSync(join(packageDirectory, 'package.json')) + } + } catch { /* keep walking */ } + const parent = dirname(directory) + if (parent === directory) return false + directory = parent + } +} + +/** Every id in one patch row's insert list, recursively (group configs included). */ +function collectInsertIds(rows: unknown[]): string[] { + const ids: string[] = [] + const walk = (value: unknown): void => { + if (!Array.isArray(value)) return + for (const entry of value) { + if (!isRecord(entry) || typeof entry.id !== 'string') continue + ids.push(entry.id) + if (Array.isArray(entry.config)) walk(entry.config) + } + } + for (const patch of rows) { + if (!isRecord(patch) || !Array.isArray(patch.insert)) continue + walk(patch.insert) + } + return ids +} + +/** DSH host core packages: what the dsh installation ships under @deepseek-ai. */ +export function corePackageNames(dshInstallDir: string | null): Set { + const names = new Set([ + // Curated fallback seed (used when the install dir cannot be located). + '@deepseek-ai/dsh', + '@deepseek-ai/dsh-base', + '@deepseek-ai/dsh-web-app', + '@deepseek-ai/dsh-headless', + '@deepseek-ai/dsh-app-boot', + '@deepseek-ai/dsh-home-paths', + '@deepseek-ai/dsh-launch-environment', + '@deepseek-ai/dsh-cmdline', + '@deepseek-ai/dsh-tools', + '@deepseek-ai/dsh-llm', + '@deepseek-ai/dsh-system-prompt', + '@deepseek-ai/dsh-attachment', + '@deepseek-ai/dsh-agent', + '@deepseek-ai/dsh-agent-loop', + '@deepseek-ai/dsh-session', + '@deepseek-ai/dsh-subagent', + '@deepseek-ai/cordis', + '@deepseek-ai/cordis-plugin-loader', + '@deepseek-ai/cordis-plugin-include', + '@deepseek-ai/cordis-plugin-hmr', + '@deepseek-ai/cordis-plugin-timer', + '@deepseek-ai/cordis-plugin-group', + ]) + if (dshInstallDir === null) return names + try { + // The install's own node_modules is the authoritative host-core inventory: + // everything under @deepseek-ai whose bare name starts with dsh or cordis, + // plus the app package itself. + for (const entry of readdirSync(join(dshInstallDir, 'node_modules', '@deepseek-ai'), { withFileTypes: true })) { + if (!entry.isDirectory() && !entry.isSymbolicLink()) continue + if (/^(?:dsh|cordis)/.test(entry.name)) names.add(`@deepseek-ai/${entry.name}`) + } + } catch { /* install node_modules unreadable — the curated seed stands */ } + try { + const manifest = JSON.parse(readFileSync(join(dshInstallDir, 'package.json'), 'utf8')) as { name?: unknown } + if (typeof manifest.name === 'string') names.add(manifest.name) + } catch { /* not a package dir */ } + return names +} + +/** Version of `name` as physically resolved at `base`/node_modules, or null. */ +function readNodeModulesVersion(base: string, name: string): string | null { + try { + const manifest = JSON.parse( + readFileSync(join(base, 'node_modules', name, 'package.json'), 'utf8'), + ) as { version?: unknown } + return typeof manifest.version === 'string' ? manifest.version : null + } catch { + return null + } +} + +/** + * Resolve one package's directory the way the dsh boot does + * (dsh-app-boot's resolveBundleDir): probe Node's own node_modules search + * paths from the installation anchor first, then the profile directory. + * Node resolution walks upward, so this also finds pnpm's workspace-root + * hoisting (`/node_modules/…` when the profile lives under + * `/`) and mirrors the Loader's package search roots. + */ +function resolvePackageDir( + anchorPackageJson: string, + name: string, + ignoredPackageDirectory?: string, +): string | null { + let paths: string[] = [] + try { + paths = createRequire(anchorPackageJson).resolve.paths(name) ?? [] + } catch { + return null + } + const ignored = ignoredPackageDirectory === undefined + ? null + : resolve(ignoredPackageDirectory) + for (const searchPath of paths) { + const candidate = join(searchPath, name) + if (ignored !== null) { + const resolvedCandidate = resolve(candidate) + const matchesIgnored = process.platform === 'win32' + ? resolvedCandidate.toLowerCase() === ignored.toLowerCase() + : resolvedCandidate === ignored + if (matchesIgnored) continue + } + if (existsSync(join(candidate, 'package.json'))) return candidate + } + return null +} + +/** + * Version of `name` visible to the profile's dependency tree: the profile's + * own node_modules first, then the workspace root (pnpm hoists shared deps + * there when the profile is a workspace member — the dsh layout keeps + * `/node_modules` as the shared store for all profiles). + */ +function readProfileVisibleVersion(profileDirectory: string, name: string): string | null { + const direct = readNodeModulesVersion(profileDirectory, name) + if (direct !== null) return direct + const workspaceRoot = dirname(profileDirectory) + if (workspaceRoot === profileDirectory) return null + return readNodeModulesVersion(workspaceRoot, name) +} + +/** Top-level installed package names (incl. scoped), excluding pnpm internals. */ +function installedPackageNames(profileDir: string): string[] { + const names: string[] = [] + // Windows `link:` installs are junctions; Dirent.isDirectory() is false for + // them on some Node versions, so treat symlinks as packages too (B2). + const isPkgDir = (entry: { isDirectory(): boolean; isSymbolicLink(): boolean }): boolean => + entry.isDirectory() || entry.isSymbolicLink() + let root: string[] + try { + root = readdirSync(join(profileDir, 'node_modules'), { withFileTypes: true }) + .filter(entry => isPkgDir(entry) && entry.name !== '.bin' && entry.name !== '.pnpm' && entry.name !== '.dsh-plugin-backups') + .map(entry => entry.name) + } catch { + return names + } + for (const name of root) { + if (!name.startsWith('@')) { + names.push(name) + continue + } + try { + for (const scoped of readdirSync(join(profileDir, 'node_modules', name), { withFileTypes: true })) { + if (isPkgDir(scoped)) names.push(`${name}/${scoped.name}`) + } + } catch { /* empty scope dir */ } + } + return names +} + +// --- semver helpers (small subset sufficient for peer range checks) --- + +interface Semver { + major: number + minor: number + patch: number + pre: string[] +} + +const SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/ + +function parseSemver(value: string): Semver | null { + const m = SEMVER_RE.exec(value.trim()) + if (m === null) return null + return { + major: Number(m[1]), + minor: Number(m[2]), + patch: Number(m[3]), + pre: m[4] === undefined ? [] : m[4].split('.'), + } +} + +function comparePre(a: string[], b: string[]): number { + const len = Math.max(a.length, b.length) + for (let i = 0; i < len; i += 1) { + const x = a[i] + const y = b[i] + if (x === undefined) return y === undefined ? 0 : -1 // a has fewer parts → a < b + if (y === undefined) return 1 + if (x === y) continue + const xn = /^\d+$/.test(x) + const yn = /^\d+$/.test(y) + if (xn && yn) return Number(x) - Number(y) || 0 + if (xn) return -1 // numeric identifiers sort before alphanumeric + if (yn) return 1 + return x < y ? -1 : 1 + } + return 0 +} + +/** Compare two semver strings: negative | zero | positive (prerelease < release of same base). */ +export function compareSemver(a: string, b: string): number { + const av = parseSemver(a) + const bv = parseSemver(b) + if (av === null || bv === null) return a < b ? -1 : a > b ? 1 : 0 + if (av.major !== bv.major) return av.major - bv.major || 0 + if (av.minor !== bv.minor) return av.minor - bv.minor || 0 + if (av.patch !== bv.patch) return av.patch - bv.patch || 0 + if (av.pre.length === 0 && bv.pre.length === 0) return 0 + if (av.pre.length === 0) return 1 // release > prerelease + if (bv.pre.length === 0) return -1 + return comparePre(av.pre, bv.pre) +} + +function gte(a: Semver, b: Semver): boolean { + const cmp = compareSemver(`${a.major}.${a.minor}.${a.patch}${a.pre.length > 0 ? `-${a.pre.join('.')}` : ''}`, `${b.major}.${b.minor}.${b.patch}${b.pre.length > 0 ? `-${b.pre.join('.')}` : ''}`) + return cmp >= 0 +} + +/** String form of a parsed Semver (the boundary objects carry no prerelease). */ +function semverStr(v: Semver): string { + return `${v.major}.${v.minor}.${v.patch}${v.pre.length > 0 ? `-${v.pre.join('.')}` : ''}` +} + +/** + * Minimal range matcher for the peer-range check: `*`, exact, ^, ~, >=, >, + * <=, <, whitespace-separated pairs, and `||` alternatives. Anything else + * returns null (unknown — reported, not asserted). + * + * Prerelease handling follows npm's semver rule, evaluated at the comparator + * SET level (one `||` alternative is one set): a version carrying a + * prerelease tag only satisfies a set when at least one comparator in that + * set shares the version's [major, minor, patch] tuple AND carries a + * prerelease of its own; then every comparator is checked normally. So + * `^0.1.0` never matches `0.2.0-rc.1` (nor `0.1.0-rc.5`), while + * `>=1.2.3-rc.1 <2.0.0` does match `1.2.3-rc.2` (issue #98 analysis). + */ +export function satisfiesRange(version: string, range: string): boolean | null { + const v = parseSemver(version) + if (v === null) return null + const versionHasPre = v.pre.length > 0 + + // Mirror pnpm's peer-dependency publish transform. Unlike ordinary + // dependency specs, a workspace token may sit inside a larger peer range. + // Operator-only (or bare) tokens receive the linked sibling's version; + // explicit versions only lose the protocol prefix. Unsupported alias/path + // forms become an unknown range below rather than a definite mismatch. + const workspaceSemver = /workspace:([\^~*]|>=|>|<=|<)?((\d+|[xX*])(\.(\d+|[xX*])){0,2})?/ + let normalizedRange = range + if (range.includes('workspace:')) { + const match = workspaceSemver.exec(range) + if (match === null) { + normalizedRange = range.replace('workspace:', '') + } else if (match[2] !== undefined) { + normalizedRange = range.replace('workspace:', '') + } else { + const operator = match[1] === '*' ? '' : (match[1] ?? '') + normalizedRange = range.replace(workspaceSemver, `${operator}${semverStr(v)}`) + } + } + + const single = (part: string): boolean | null => { + const p = part.trim() + if (p === '' || p === '*' || p === 'x' || p === 'X') return true + const m = /^(\^|~|>=|<=|>|<)?(.*)$/.exec(p) + const op = m?.[1] ?? '' + const target = (m?.[2] ?? '').trim() + const tv = parseSemver(target) + if (tv === null) return null + const major = tv.major + const minor = tv.minor + const patch = tv.patch + switch (op) { + case '': + return compareSemver(version, target) === 0 + case '>=': + return gte(v, tv) + case '<=': + return gte(tv, v) + case '>': + return compareSemver(version, target) > 0 + case '<': + return compareSemver(version, target) < 0 + case '^': { + // npm caret semantics: >= given, strictly < the next breaking bump. + const upper: Semver = major > 0 + ? { major: major + 1, minor: 0, patch: 0, pre: [] } + : minor > 0 + ? { major: 0, minor: minor + 1, patch: 0, pre: [] } + : { major: 0, minor: 0, patch: patch + 1, pre: [] } + return gte(v, tv) && compareSemver(semverStr(upper), version) > 0 + } + case '~': { + // npm tilde semantics: >= given, strictly < the next minor. + const upper: Semver = { major, minor: minor + 1, patch: 0, pre: [] } + return gte(v, tv) && compareSemver(semverStr(upper), version) > 0 + } + default: + return null + } + } + + /** Parse one comparator part into op + target; null when unknown. */ + const comparator = (part: string): { op: string; target: string } | null => { + const p = part.trim() + if (p === '' || p === '*' || p === 'x' || p === 'X') return { op: '', target: '' } + const m = /^(\^|~|>=|<=|>|<)?(.*)$/.exec(p) + if (m === null) return null + const target = (m[2] ?? '').trim() + // Enforce this function's documented unknown-range contract before the + // prerelease admission gate. Otherwise an unknown protocol such as + // `workspace:^` or `catalog:default` is misreported as a definite false + // whenever the resolved version happens to carry a prerelease tag. + if (parseSemver(target) === null) return null + return { op: m[1] ?? '', target } + } + + /** Evaluate ONE comparator set (a `||` alternative) as a conjunction. */ + const evaluateSet = (set: string): boolean | null => { + const parts = set.trim().split(/\s+/).filter(part => part !== '') + if (parts.length === 0) return true + const parsed = parts.map(part => comparator(part)) + if (parsed.some(part => part === null)) return null + if (versionHasPre) { + // npm gate (set-level): the set admits prerelease versions only when a + // comparator pins the SAME base tuple with a prerelease of its own. + const admitted = parsed.some((part) => { + if (part?.target === '') return false + const tv = parseSemver(part?.target ?? '') + return tv !== null && tv.pre.length > 0 + && v.major === tv.major && v.minor === tv.minor && v.patch === tv.patch + }) + if (!admitted) return false + } + const results = parsed.map(part => single(part?.op !== undefined ? `${part.op}${part.target}` : '')) + if (results.some(r => r === null)) return null + return results.every(r => r === true) + } + + if (normalizedRange.includes('||')) { + const outcomes = normalizedRange.split('||').map(part => evaluateSet(part)) + if (outcomes.some(out => out === true)) return true + if (outcomes.some(out => out === null)) return null + return false + } + return evaluateSet(normalizedRange) +} + +// --- composition (mirrors dsh-app-boot applyEntryPatches) --- + +interface EntryNode { + id: string + name?: string + layer?: string + group?: boolean + config?: unknown + disabled?: unknown +} + +/** Flatten a tree of entries (group configs included) into row records. */ +function flattenEntries(nodes: EntryNode[]): LoaderRow[] { + const rows: LoaderRow[] = [] + const walk = (list: EntryNode[], inheritedLayer?: string): void => { + for (const node of list) { + // Nested group configs come straight from the parsed patch and do not + // carry the synthetic layer metadata attached to their parent insert. + const layer = typeof node.layer === 'string' ? node.layer : inheritedLayer + if (layer === undefined) continue + rows.push({ id: node.id, layer, kind: 'insert', name: node.name }) + if (node.group === true && Array.isArray(node.config)) { + walk(node.config as EntryNode[], layer) + } + } + } + walk(nodes) + return rows +} + +type DisabledState = 'active' | 'disabled' | 'conditional' + +interface ResolvableLoaderRow extends LoaderRow { + activation: 'required' | 'conditional' +} + +/** Literal values use Loader Boolean semantics; `!!js` stays indeterminate. */ +function disabledState(value: unknown): DisabledState { + if (isRecord(value) && typeof value.__jsExpr === 'string') return 'conditional' + return Boolean(value) ? 'disabled' : 'active' +} + +function combineDisabled(parent: DisabledState, own: DisabledState): DisabledState { + if (parent === 'disabled' || own === 'disabled') return 'disabled' + if (parent === 'conditional' || own === 'conditional') return 'conditional' + return 'active' +} + +/** + * Rows whose specifiers the Loader can attempt to import. Group rows are + * always imported even when disabled (their disabled state gates children), + * while expression-gated non-group rows are retained as conditional. + */ +function resolvableEntries(nodes: EntryNode[]): ResolvableLoaderRow[] { + const rows: ResolvableLoaderRow[] = [] + const walk = ( + list: EntryNode[], + inheritedLayer?: string, + parentDisabled: DisabledState = 'active', + ): void => { + for (const node of list) { + const layer = typeof node.layer === 'string' ? node.layer : inheritedLayer + if (layer === undefined) continue + const descendantsDisabled = combineDisabled(parentDisabled, disabledState(node.disabled)) + const activation = node.group === true ? 'required' : descendantsDisabled + if (activation !== 'disabled') { + rows.push({ + id: node.id, + layer, + kind: 'insert', + name: node.name, + activation: activation === 'conditional' ? 'conditional' : 'required', + }) + } + if (node.group === true && Array.isArray(node.config)) { + walk(node.config as EntryNode[], layer, descendantsDisabled) + } + } + } + walk(nodes) + return rows +} + +export interface LayerInput { + label: string + kind: 'bundle' | 'user' | 'home' + patches: unknown[] + parseError: string | null +} + +interface Composed { + rows: LoaderRow[] + resolvableRows: ResolvableLoaderRow[] + duplicates: DuplicateId[] + overrides: OverrideRow[] + orphans: OrphanRow[] +} + +/** + * Apply the layer stack over an empty root exactly like the dsh boot include. + * Exported so the trial-start validation (src/trial.ts) can replay the + * composition with a candidate bundle order BEFORE anything is written. + */ +export function composeLayers(layers: LayerInput[]): Composed { + const tree: EntryNode[] = [] + const orphans: OrphanRow[] = [] + const overrides: OverrideRow[] = [] + /** + * The boot's entryMap, mirrored incrementally: the LAST row registered for + * an id (top-level or nested group member) is the patch target, and later + * inserts overwrite the map entry — exactly dsh-app-boot's applyEntryPatches + * buildMap. Keeping the map instead of re-walking the tree pins the + * duplicate-id resolution to the boot's behavior (issue #98 analysis: + * explicit composition boundaries). + */ + const entryMap = new Map() + const buildMap = (nodes: EntryNode[]): void => { + for (const node of nodes) { + if (node.id !== '') entryMap.set(node.id, node) + if (node.group === true && Array.isArray(node.config)) buildMap(node.config as EntryNode[]) + } + } + for (const layer of layers) { + for (const patch of layer.patches) { + if (!isRecord(patch)) continue + const { id, insert, name, ...overridesOf } = patch + // Boot boundary: `insert` and `id` are truthiness-checked, so a falsy + // `insert` (null/''/0) falls through to the patch path and an empty id + // makes an insert a plain top-level append (applyEntryPatches). + const hasId = typeof id === 'string' ? id !== '' : Boolean(id) + const lookupKey = hasId ? String(id) : '' + if (insert) { + if (!Array.isArray(insert)) { + orphans.push({ id: lookupKey === '' ? '(anonymous)' : lookupKey, layer: layer.label, reason: 'insert is not an array' }) + continue + } + const nodes = (insert as unknown[]).filter(isRecord).map((entry): EntryNode | null => { + if (typeof entry.id !== 'string') return null + return { + id: entry.id, + name: typeof entry.name === 'string' ? entry.name : undefined, + layer: layer.label, + group: entry.group === true, + config: Array.isArray(entry.config) ? entry.config : undefined, + disabled: entry.disabled, + } + }).filter((n): n is EntryNode => n !== null) + if (hasId) { + const target = entryMap.get(lookupKey) + if (target === undefined) { + orphans.push({ id: lookupKey, layer: layer.label, reason: 'insert target not found' }) + continue + } + if (target.group !== true) { + orphans.push({ id: lookupKey, layer: layer.label, reason: 'insert target is not a group' }) + continue + } + // Boot boundary: a group with a non-array config is fixed up to an + // empty array before the append (applyEntryPatches does the same). + if (!Array.isArray(target.config)) target.config = [] + target.config = [...(target.config as unknown[]), ...nodes] + } else { + tree.push(...nodes) + } + buildMap(nodes) + continue + } + if (!hasId) { + orphans.push({ id: '(anonymous)', layer: layer.label, reason: 'id required for non-insert patch' }) + continue + } + const target = entryMap.get(lookupKey) + if (target === undefined) { + orphans.push({ id: lookupKey, layer: layer.label, reason: 'patch target not found' }) + continue + } + // Boot boundary: the name guard is truthiness-based — an empty-string + // name on the patch row does not trigger the mismatch skip. + if (name && name !== target.name) { + orphans.push({ id: lookupKey, layer: layer.label, reason: `name mismatch (expected ${String(target.name)}, got ${String(name)})` }) + continue + } + const priorLayers: string[] = [] + for (const node of flattenEntries(tree)) { + if (node.id === lookupKey && !priorLayers.includes(node.layer)) priorLayers.push(node.layer) + } + if (priorLayers.some(prior => prior !== layer.label)) { + overrides.push({ id: lookupKey, layer: layer.label, overriddenLayers: priorLayers.filter(prior => prior !== layer.label) }) + } + for (const [key, value] of Object.entries(overridesOf)) { + if (key === 'id') continue + ;(target as unknown as Record)[key] = value + } + } + } + const rows = flattenEntries(tree) + const resolvableRows = resolvableEntries(tree) + const byId = new Map() + for (const row of rows) { + const layers = byId.get(row.id) ?? [] + if (!layers.includes(row.layer)) layers.push(row.layer) + byId.set(row.id, layers) + } + const duplicates: DuplicateId[] = [] + const counts = new Map() + for (const row of rows) counts.set(row.id, (counts.get(row.id) ?? 0) + 1) + for (const [id, count] of counts) { + if (count < 2) continue + duplicates.push({ id, layers: byId.get(id) ?? [], count }) + } + duplicates.sort((a, b) => a.id.localeCompare(b.id)) + return { rows, resolvableRows, duplicates, overrides, orphans } +} + +/** Distinct versions of `@deepseek-ai/{dsh,cordis}*` packages in the lockfile. */ +function lockfileCoreVersions(profileDir: string): Map { + const found = new Map>() + let text: string + try { + text = readFileSync(join(profileDir, 'pnpm-lock.yaml'), 'utf8') + } catch { + return new Map() + } + for (const m of text.matchAll(/(@deepseek-ai\/(?:dsh|cordis)[^@\s'"]*?)@([0-9][^\s:'"()]*)/g)) { + const name = m[1] ?? '' + // pnpm v9 peer-resolution keys carry a suffix: `name@1.0.0(peer@x)`. The + // version capture stops at the `(` — and at `)` too, so the peer + // reference INSIDE the suffix (`name@1.0.0(@deepseek-ai/dsh-base@4.0.1)`) + // never yields a fake `4.0.1)` version that would invent a phantom + // multi-version report on a healthy profile (issue #98 analysis). + const version = m[2] ?? '' + // Registry resolutions only: skip link:/git forms and anything that is + // not a well-formed semver (parseSemver double-checks the capture). + if (parseSemver(version) === null) continue + const versions = found.get(name) ?? new Set() + versions.add(version) + found.set(name, versions) + } + const out = new Map() + for (const [name, versions] of found) out.set(name, [...versions].sort(compareSemver)) + return out +} + +/** + * Build the bundle layer stack for a profile under a GIVEN bundle order — + * the manifest order for analyzeProfile, or a candidate order for trial + * validation (src/trial.ts). Bundle resolution mirrors the boot exactly: + * the dsh installation anchor first (in-box bundles always come from the + * running dsh, never a profile-local copy), then Node's module search from + * the profile directory (covers community bundles and pnpm workspace-root + * hoisting). A single code path keeps the check report and the trial + * validation from ever disagreeing about what a bundle is or where it lives. + */ +export function buildBundleLayers( + profileDirectory: string, + bundleNames: string[], + specs: Record, + dshInstallDir: string | null, +): { bundles: BundleLayer[]; layers: LayerInput[] } { + const bundles: BundleLayer[] = bundleNames.map((name) => { + // The real loader gives the DSH installation first refusal for in-box + // bundles. Desktop keeps that installation private from plugins, so a + // DIRECT profile-local copy with the same official name is only a stale + // shadow, never evidence for the layer the running host loaded (#371). + // Keep walking the profile anchor's parent search paths: Desktop heals an + // authoritative host fallback at /node_modules. + const ignoredProfilePackage = dshInstallDir === null && INBOX_BUNDLES.has(name) + ? join(profileDirectory, 'node_modules', name) + : undefined + const anchors: Array<{ anchor: string | null; ignoredPackageDirectory?: string }> = [ + { anchor: dshInstallDir !== null ? join(dshInstallDir, 'package.json') : null }, + { + anchor: join(profileDirectory, 'package.json'), + ignoredPackageDirectory: ignoredProfilePackage, + }, + ] + let directory: string | null = null + for (const { anchor, ignoredPackageDirectory } of anchors) { + if (anchor === null) continue + directory = resolvePackageDir(anchor, name, ignoredPackageDirectory) + if (directory !== null) break + } + const layer: BundleLayer = { + name, + source: specs[name] ?? '(not a direct dependency)', + kind: INBOX_BUNDLES.has(name) ? 'official' : 'community', + directory, + patchPath: null, + error: null, + entries: [], + parseError: null, + } + if (directory === null) { + // An in-box bundle is supplied by the dsh INSTALLATION, not by the + // profile — that is what makes it in-box. So failing to find one says + // we could not locate the installation, not that the profile is + // broken: unusual or older hosts can still hide the in-box installation + // from both the CLI-entry and packaged-Desktop discovery anchors. + // + // Calling that "will fail to boot" turned a working composition into a + // fatal verdict and rolled back a good update (#369) — while `dsh + // --dump-config` on the same profile exited 0. Unknown has to read as + // unknown; the profile's own bundles are still judged normally. + if (INBOX_BUNDLES.has(name)) { + layer.error = null + layer.unresolvedInbox = true + return layer + } + layer.error = 'bundle package is not installed — the profile will fail to boot' + return layer + } + let bundleManifest: { dsh?: { bundle?: { patch?: unknown; order?: unknown } } } + try { + bundleManifest = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8')) as typeof bundleManifest + } catch { + layer.error = 'bundle package.json is unreadable' + return layer + } + const declared = bundleManifest.dsh?.bundle?.patch + if (typeof declared !== 'string') { + layer.error = 'bundle declares no dsh.bundle.patch — the profile will fail to boot' + return layer + } + const patchPath = join(directory, declared) + if (!existsSync(patchPath)) { + layer.error = `declared patch ${declared} is missing — the profile will fail to boot` + return layer + } + layer.patchPath = patchPath + const patches = parsePatchFile(patchPath) + if (patches === null) { + layer.parseError = 'patch file is not a valid entry list' + return layer + } + layer.entries = collectInsertIds(patches) + const order = bundleManifest.dsh?.bundle?.order + if (order !== null && typeof order === 'object' && !Array.isArray(order)) { + const listOf = (value: unknown): string[] | undefined => Array.isArray(value) + ? value.filter((item): item is string => typeof item === 'string') + : undefined + const after = listOf((order as Record).after) + const before = listOf((order as Record).before) + if (after !== undefined || before !== undefined) { + layer.order = { ...(before !== undefined ? { before } : {}), ...(after !== undefined ? { after } : {}) } + } + } + return layer + }) + const layers: LayerInput[] = bundles.map((bundle) => ({ + label: bundle.name, + kind: 'bundle' as const, + patches: bundle.patchPath !== null && bundle.parseError === null ? parsePatchFile(bundle.patchPath) ?? [] : [], + parseError: bundle.parseError, + })) + return { bundles, layers } +} + +/** + * Analyze one profile directory (issue #98, phase 1). Pure function of the + * directory contents — safe to call on every market open. + */ +export function analyzeProfile(profileDirectory: string, options: CheckOptions = {}): CheckReport { + const dshInstall = options.dshInstallDir ?? findDshInstallDir() + const home = resolveDshHome(options.homeDir) + const core = corePackageNames(dshInstall) + + // --- 1. bundle stack --- + const manifest = (() => { + try { + return JSON.parse(readFileSync(join(profileDirectory, 'package.json'), 'utf8')) as { + dependencies?: Record + dsh?: { profile?: { bundles?: unknown } } + } + } catch { + return null + } + })() + const bundleNames = Array.isArray(manifest?.dsh?.profile?.bundles) + ? manifest.dsh.profile.bundles.filter((name): name is string => typeof name === 'string') + : [] + const specs = manifest?.dependencies ?? {} + const built = buildBundleLayers(profileDirectory, bundleNames, specs, dshInstall) + const bundles = built.bundles + const bundleLayers = built.layers + + // --- 2. composed loader rows / duplicates / overrides / orphans --- + const layers: LayerInput[] = [...bundleLayers] + const userPatchPath = join(profileDirectory, 'cordis.patch.yml') + if (existsSync(userPatchPath)) { + const patches = parsePatchFile(userPatchPath) + layers.push({ label: 'user-patch', kind: 'user', patches: patches ?? [], parseError: patches === null ? 'patch file is not a valid entry list' : null }) + } + const homePatchPath = join(home, 'cordis.patch.yml') + if (existsSync(homePatchPath)) { + const patches = parsePatchFile(homePatchPath) + layers.push({ label: 'home-patch', kind: 'home', patches: patches ?? [], parseError: patches === null ? 'patch file is not a valid entry list' : null }) + } + const composed = composeLayers(layers) + + // --- 3. peer dependency mismatches (every declared peer, core or not) --- + // NOTE (issue #98 division of labor): the core-package-as-ordinary- + // dependency check (coreDeps / shadowing) belongs to @yzke's manifest PR; + // PR-A keeps only the peer-resolution check here. + const peerMismatches: PeerMismatch[] = [] + const seenDeps = new Set() + for (const plugin of installedPackageNames(profileDirectory)) { + let pkg: { + peerDependencies?: Record + peerDependenciesMeta?: Record + } + try { + pkg = JSON.parse(readFileSync(join(profileDirectory, 'node_modules', plugin, 'package.json'), 'utf8')) as typeof pkg + } catch { + continue + } + const pluginDir = join(profileDirectory, 'node_modules', plugin) + const map = pkg.peerDependencies + if (map === null || typeof map !== 'object') continue + for (const [name, spec] of Object.entries(map)) { + if (typeof spec !== 'string') continue + const key = `${plugin}\u0000${name}\u0000peer` + if (seenDeps.has(key)) continue + seenDeps.add(key) + const hoisted = readProfileVisibleVersion(profileDirectory, name) + const nested = readNodeModulesVersion(pluginDir, name) + const host = dshInstall !== null ? readNodeModulesVersion(dshInstall, name) : null + // Node resolves a plugin's peer from its OWN node_modules first + // (nested), then the profile tree (hoisted), then the host install. + const resolved = nested ?? hoisted ?? host + // Peer checks cover EVERY declared peer, not just host core packages: + // plugin-to-plugin peer mismatches break runtime registration just as + // hard (issue #98 optimization round). + const satisfied = resolved !== null ? satisfiesRange(resolved, spec) : null + const optional = pkg.peerDependenciesMeta?.[name]?.optional === true + peerMismatches.push({ + plugin, name, range: spec, resolved, + satisfied: satisfied === null ? null : satisfied, + ...(optional ? { optional: true } : {}), + }) + } + } + + // --- 4. multi-version core packages from the lockfile --- + const multiVersion: MultiVersion[] = [] + for (const [name, versions] of lockfileCoreVersions(profileDirectory)) { + if (versions.length < 2) continue + multiVersion.push({ name, versions, hoisted: readProfileVisibleVersion(profileDirectory, name) }) + } + multiVersion.sort((a, b) => a.name.localeCompare(b.name)) + + // --- summary --- + const errors: string[] = [] + const warnings: string[] = [] + for (const bundle of bundles) { + if (bundle.error !== null) errors.push(`bundle ${bundle.name}: ${bundle.error}`) + if (bundle.parseError !== null) errors.push(`bundle ${bundle.name}: ${bundle.parseError}`) + } + for (const layer of layers) { + if (layer.parseError !== null && layer.kind !== 'bundle') errors.push(`${layer.label}: ${layer.parseError}`) + } + // User and home patches can insert packages independently of a bundle. + // Only check rows that survived composition: an insert targeting a missing + // group is skipped by the boot and therefore cannot cause module loading to + // fail. Normalize package subpaths to their npm root and check the profile's + // node_modules ancestry; exact exports/subpath validation and relative, + // absolute, URL, builtin, package-import (#), or cordis: specifiers are + // outside this check. + const userLayerLabels = new Set( + layers + .filter(layer => layer.kind === 'user' || layer.kind === 'home') + .map(layer => layer.label), + ) + const candidates = new Map() + for (const row of composed.resolvableRows) { + if (row.kind !== 'insert' || !userLayerLabels.has(row.layer)) continue + if (row.name === undefined || row.name === '') { + const message = `${row.layer}: loader entry ${JSON.stringify(row.id)} has no module name` + if (row.activation === 'required') errors.push(`${message} — the profile will fail to boot`) + else warnings.push(`${message} — boot will fail if its disabled expression enables the entry`) + continue + } + if (!isBareLoaderSpecifier(row.name)) continue + if (isBuiltin(row.name)) continue + const packageName = packageRoot(row.name) + if (packageName === null) { + const message = `${row.layer}: loader specifier ${JSON.stringify(row.name)} is not a valid bare package name` + if (row.activation === 'required') errors.push(`${message} — the profile will fail to boot`) + else warnings.push(`${message} — boot will fail if its disabled expression enables the entry`) + continue + } + const key = `${row.layer}\u0000${packageName}` + const previous = candidates.get(key) + if (previous === undefined || previous.row.activation === 'conditional' && row.activation === 'required') { + candidates.set(key, { row, packageName }) + } + } + for (const { row, packageName } of candidates.values()) { + if (profilePackageInstalled(profileDirectory, packageName)) continue + const message = `${row.layer}: loader package ${packageName} is not installed in the profile` + if (row.activation === 'required') errors.push(`${message} — the profile will fail to boot`) + else warnings.push(`${message} — boot will fail if its disabled expression enables the entry`) + } + for (const dup of composed.duplicates) { + errors.push(`duplicate loader entry id ${JSON.stringify(dup.id)} (${dup.count} rows: ${dup.layers.join(', ')})`) + } + for (const orphan of composed.orphans) { + warnings.push(`${orphan.layer}: ${orphan.id} — ${orphan.reason}`) + } + for (const mismatch of peerMismatches) { + // Only CONFIRMED incompatibilities warn. Un-evaluable peers (sat=null — + // the peer is supplied by the host, an optional accelerator, or simply + // absent) stay in the peerMismatches list for the UI but are not noise + // in the summary (issue #98 optimization round). + // ...and an OPTIONAL peer is not a confirmed incompatibility either. + // classifyPeer already downgrades those to non-risk (compatibility.ts), + // and the summary disagreeing with it meant a plugin that declares "I + // work without this" still produced a scary warning line — including + // for the market's own optional peer, on every profile that installs us + // (#275 by @tjxjxjx). + if (mismatch.satisfied === false && mismatch.optional !== true) { + warnings.push(`${mismatch.plugin} peer range ${mismatch.name}@${mismatch.range} does not match resolved ${String(mismatch.resolved)}`) + } + } + for (const mv of multiVersion) { + const line = `${mv.name}: ${mv.versions.join(' / ')}${mv.hoisted !== null ? ` (hoisted ${mv.hoisted})` : ''}` + if (core.has(mv.name)) errors.push(`multiple versions of core package — ${line}`) + else warnings.push(`multiple versions of ${line}`) + } + // Current-order before/after rule conflicts (issue #98 phase 2): these are + // informative for the ordering UI; the author-declared rule breaking the + // CURRENT stack is worth a warning but not a boot failure. Conflicts are + // exposed both at the report top level and per bundle (order.conflicts) so + // the ordering panel can render them next to the bundle rows. + const orderConflicts = validateOrder(bundleNames, readBundleRules(profileDirectory)) + for (const conflict of orderConflicts) { + warnings.push(`${conflict.name}: ${conflict.reason}`) + } + for (const bundle of bundles) { + const own = orderConflicts.filter(conflict => conflict.name === bundle.name) + if (own.length > 0) bundle.order = { ...bundle.order, conflicts: own } + } + // LOOT-style auto-fix: suggest a minimal-change order satisfying every + // declared before/after rule. No rules → no suggestion (nothing to fix); + // with rules the suggestion keeps unconstrained bundles in their current + // relative order (issue #125 review — never silently rewrites a hand-picked + // order into an arbitrary canonical one). + const suggestedOrder = suggestOrder(bundleNames, readBundleRules(profileDirectory)) + if (suggestedOrder === null) { + // No declared rules — nothing to suggest and nothing to warn about. + } else if (!suggestedOrder.ok) { + warnings.push(`ordering constraints contain a cycle: ${suggestedOrder.cycle.join(' -> ')} — no compliant order exists / 排序约束存在循环依赖,无法得出合规顺序`) + } else { + // Only warn when the CURRENT order actually breaks a declared rule. A + // hand-picked order that satisfies every rule but merely differs from the + // suggestion is valid — flagging it would be a false alert on a healthy + // profile (issue #98 analysis). + if (orderConflicts.length > 0) { + warnings.push('current bundle order violates declared rules — a better order is suggested / 当前 bundle 顺序违反声明规则,已给出更优顺序') + } + } + + // Duplicate loader NAMES: the Loader registers plugins by name, so two rows + // with the same name in DIFFERENT layers shadow each other at runtime (the + // later layer wins). Rows sharing a name within ONE layer are routine — a + // single bundle may define several entries under the same name — and are + // skipped entirely: a same-layer "duplicate name" is a false positive, not + // a conflict. The hard conflict is duplicate loader entry ids + // (report.duplicates), which fail the boot outright and are unchanged. + const nameCounts = new Map() + for (const row of composed.rows) { + if (row.name === undefined) continue + const layers = nameCounts.get(row.name) ?? [] + if (!layers.includes(row.layer)) layers.push(row.layer) + nameCounts.set(row.name, layers) + } + const duplicateNames: DuplicateName[] = [] + for (const [name, layers] of nameCounts) { + // Only cross-layer collisions are real shadowing candidates: all rows + // with this name living in one layer is a normal multi-entry bundle. + if (layers.length < 2) continue + const count = composed.rows.filter(row => row.name === name).length + duplicateNames.push({ name, layers, count }) + // Deliberately NOT pushed into summary.warnings: the report carries the + // shadowing rows structurally (duplicateNames, rendered by the + // diagnostics panel), and the cost of a false positive — a healthy + // profile flagged with a warning — outweighs a false negative here. + } + duplicateNames.sort((a, b) => a.name.localeCompare(b.name)) + + return { + profile: profileDirectory, + scannedAt: Date.now(), + bundles, + rows: composed.rows, + duplicates: composed.duplicates, + duplicateNames, + overrides: composed.overrides, + orphans: composed.orphans, + peerMismatches, + multiVersion, + orderConflicts, + suggestedOrder, + summary: { + ok: errors.length === 0, + errors, + warnings, + }, + } +} diff --git a/packages/dshmarket/src/client/CommentsModal.tsx b/packages/dshmarket/src/client/CommentsModal.tsx new file mode 100644 index 000000000..4759169a4 --- /dev/null +++ b/packages/dshmarket/src/client/CommentsModal.tsx @@ -0,0 +1,116 @@ +/** + * The comment thread for one plugin, rendered inside the market. + * + * Opening this is the request to read the comments, so it loads on open — + * there is no second click to confirm what the first one already said. What + * the reader does have to be told, once and plainly, is that reading here + * contacts giscus.app and GitHub: nothing else in the market talks to a third + * party at the reader's request, and a comment box that quietly opens that + * connection would be a surprise, not a feature. + * + * The thread itself lives in GitHub Discussions, keyed on the same + * `plugin:` term the two websites use — see comments.ts. + */ + +import { useEffect, useRef, useState } from 'react' +import { Button, Modal } from '@deepseek-ai/dsh-client-ui-primitives' +import css from './Market.module.css' +import { GISCUS, commentsTerm, giscusLang } from './comments.ts' + +/** + * Whether the host is currently showing a dark theme. + * + * Read off the rendered page rather than from `prefers-color-scheme`: the host + * lets the reader pick a theme (including custom ones) independently of the + * OS, and a light comment box dropped into a dark window is the kind of seam + * that makes an embed look bolted on. + */ +function hostIsDark(): boolean { + if (typeof window === 'undefined') return false + const bg = getComputedStyle(document.body).backgroundColor + const m = /rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/.exec(bg) + if (!m) return window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false + const [r, g, b] = [Number(m[1]), Number(m[2]), Number(m[3])] + return (0.299 * r + 0.587 * g + 0.114 * b) < 128 +} + +export function CommentsModal(props: { + name: string + url: string + lang: string + onClose: () => void + t: (k: string) => string +}) { + const { name, url, lang, onClose, t } = props + const mount = useRef(null) + const [state, setState] = useState<'loading' | 'ready' | 'failed'>('loading') + // Bumping this re-runs the effect, which is what "retry" means here. + const [attempt, setAttempt] = useState(0) + + useEffect(() => { + const host = mount.current + if (host === null) return + host.replaceChildren() + setState('loading') + + const script = document.createElement('script') + script.src = 'https://giscus.app/client.js' + script.async = true + script.crossOrigin = 'anonymous' + Object.assign(script.dataset, { + repo: GISCUS.repo, + repoId: GISCUS.repoId, + category: GISCUS.category, + categoryId: GISCUS.categoryId, + mapping: 'specific', + term: commentsTerm(url), + // `strict` hashes the term, so `owner/repo` cannot collide with a + // `owner/repo--sub` entry from the same repository. + strict: '1', + reactionsEnabled: '1', + emitMetadata: '0', + inputPosition: 'bottom', + theme: hostIsDark() ? 'dark' : 'light', + lang: giscusLang(lang), + loading: 'lazy', + }) + script.onload = () => setState('ready') + script.onerror = () => { script.remove(); setState('failed') } + host.append(script) + + return () => { host.replaceChildren() } + }, [url, lang, attempt]) + + return ( + +

{t('commentsNote')}

+ {state === 'failed' + ? ( +
+

{t('commentsError')}

+ + + {t('commentsOnGitHub')} + +
+ ) + : ( +

+ {state === 'loading' ? t('commentsLoading') : ''} +

+ )} +
+ + ) +} diff --git a/packages/dshmarket/src/client/Diagnostics.tsx b/packages/dshmarket/src/client/Diagnostics.tsx new file mode 100644 index 000000000..a4f89b10f --- /dev/null +++ b/packages/dshmarket/src/client/Diagnostics.tsx @@ -0,0 +1,907 @@ +/** + * Diagnostics tab — issue #98: renders the profile composition check report + * served by the host route /dsh-market/check (see src/check.ts). Below the + * report sit the phase 2/3 action panels: a community-bundle ordering block + * (reorder locally with ↑/↓, POST to /dsh-market/bundle-order) and a + * snapshots & rollback panel (snapshot-panel.tsx) — collapsible, default + * collapsed, and lazy-fetching on first expand. The plugin presets panel + * ships in a later stacked PR. + * + * Read-only view of the loading-layer stack and the conflict surface: bundle + * order (official vs community), duplicate loader entry ids, peer dependency + * mismatches, multi-version core packages, overrides and orphan patches. The + * report + * shape mirrors the CheckReport interface in src/check.ts; it is re-declared + * here because the client bundle is built independently of the host tree. + */ +import { useCallback, useEffect, useMemo, useRef, useState, type DragEvent, type ReactNode } from 'react' +import { Button, IconChevronDownOutline14, IconChevronRightOutline14, IconLoadingOutline16, IconRefreshOutline14, StateDot } from '@deepseek-ai/dsh-client-ui-primitives' +import css from './Market.module.css' +import { api } from './market-data.ts' +import type { Translate } from './market-data.ts' +import { PresetPanel } from './preset-panel.tsx' +import { SnapshotPanel } from './snapshot-panel.tsx' + +/** Mirrors BundleLayer in src/check.ts. */ +interface BundleLayer { + name: string + source: string + kind: 'official' | 'community' + directory: string | null + patchPath: string | null + error: string | null + entries: string[] + parseError: string | null +} + +/** Mirrors DuplicateId in src/check.ts. */ +interface DuplicateId { + id: string + layers: string[] + count: number +} + +/** Mirrors OverrideRow in src/check.ts. */ +interface OverrideRow { + id: string + layer: string + overriddenLayers: string[] +} + +/** Mirrors OrphanRow in src/check.ts. */ +interface OrphanRow { + id: string + layer: string + reason: string +} + +/** Mirrors PeerVerdict in src/check.ts. */ +type PeerVerdict = + | { + kind: 'risk' + risk: { plugin: string; peer: string; range: string; resolved: string; direction: 'belowMin' | 'aboveMax' } + } + | { + kind: 'warning' + warning: { plugin: string; peer: string; range: string; resolved: string; reason: 'aboveMax' | 'optional' } + } + | { kind: 'none' } + +/** Mirrors PeerMismatch in src/check.ts. */ +interface PeerMismatch { + plugin: string + name: string + range: string + resolved: string | null + satisfied: boolean | null + optional?: boolean + verdict?: PeerVerdict +} + +/** Mirrors MultiVersion in src/check.ts. */ +interface MultiVersion { + name: string + versions: string[] + hoisted: string | null +} + +/** Mirrors CheckSummary in src/check.ts. */ +interface CheckSummary { + ok: boolean + errors: string[] + warnings: string[] +} + +/** Mirrors OrderConflict in src/order.ts (top-level orderConflicts in CheckReport). */ +interface OrderConflict { + name: string + reason: string +} + +/** Mirrors CheckReport in src/check.ts. */ +interface CheckReport { + profile: string + scannedAt: number + bundles: BundleLayer[] + duplicates: DuplicateId[] + overrides: OverrideRow[] + orphans: OrphanRow[] + peerMismatches: PeerMismatch[] + multiVersion: MultiVersion[] + summary: CheckSummary + /** #98 phase 2: validateOrder result for the CURRENT bundle order, when the host emits it. */ + orderConflicts?: OrderConflict[] + /** #98 opt: loader rows sharing one name — informational display only, never a conflict (review #109). */ + duplicateNames?: Array<{ name: string; layers: string[]; count: number }> + /** #98 opt: LOOT-style suggested community order satisfying every rule. */ + suggestedOrder?: { ok: true; order: string[] } | { ok: false; cycle: string[] } | null +} + +/** + * A collapsible report section: header shows title + count + chevron; the + * body stays mounted (hidden via CSS when collapsed) so every block keeps + * its state. ALL blocks are collapsed by default — the summary strip above + * gives the overview, and a problem block's title is highlighted and its + * collapsed `overview` line shows the first issue, so nothing important is + * hidden. Expand a block to see its full content. + */ +function Section(props: { + title: string + count: number + empty: string + defaultOpen?: boolean + problem?: boolean + overview?: ReactNode + /** Render `children` even at count 0 (e.g. a zero-mismatch peer block whose + * informational disclosure must stay reachable). */ + alwaysShowBody?: boolean + children: ReactNode +}) { + const { title, count, empty, defaultOpen, problem = true, overview, alwaysShowBody = false, children } = props + const [open, setOpen] = useState(defaultOpen ?? false) + const alert = problem && count > 0 + return ( +
+ + {!open && overview !== undefined &&
{overview}
} +
+ {count === 0 && !alwaysShowBody ?
{empty}
: children} +
+
+ ) +} + +/** A collapsible section that KEEPS its children mounted (hidden via CSS when + * collapsed) so the phase 3 panels below retain their loaded data and + * in-progress edits across collapses. Children mount from the start, but the + * panels only fetch when `open` first becomes true. + */ +function CollapsibleSection(props: { title: string; count?: number; open: boolean; onToggle: () => void; children: ReactNode }) { + const { title, count, open, onToggle, children } = props + return ( +
+ +
+ {children} +
+
+ ) +} +/** Map an orphan patch reason (src/check.ts) to a locale key for its badge. */ +function orphanKindLabel(reason: string): string { + if (reason === 'insert is not an array') return 'orphanInsertNotArray' + if (reason === 'insert target not found') return 'orphanInsertTargetMissing' + if (reason === 'insert target is not a group') return 'orphanInsertTargetNotGroup' + if (reason === 'id required for non-insert patch') return 'orphanIdRequired' + if (reason === 'patch target not found') return 'orphanPatchTargetMissing' + if (reason.startsWith('name mismatch')) return 'orphanNameMismatch' + return 'orphanReasonOther' +} + +/** Badge label for one risk-tier peer row (#201). */ +function peerRiskLabel(peer: PeerMismatch, t: Translate): string { + return peer.verdict?.kind === 'risk' && peer.verdict.risk.direction === 'aboveMax' + ? t('peerRiskAboveMax') + : t('peerRiskBelowMin') +} + +/** Badge label for one warning-tier peer row (optional / aboveMax / legacy). */ +function peerWarningLabel(peer: PeerMismatch, t: Translate): string { + if (peer.verdict?.kind !== 'warning') return t('checkUnsatisfied') + return peer.verdict.warning.reason === 'optional' ? t('peerWarnOptional') : t('peerWarnAboveMax') +} + +/** + * Fetch and render the profile check report. Refetches on every mount, so + * switching tabs away and back re-runs the (cheap, read-only) analysis; the + * phase 3 panels below call `refresh()` after applying changes. + */ +export function Diagnostics(props: { t: Translate }) { + const { t } = props + const [report, setReport] = useState(null) + const [error, setError] = useState(null) + const [orderOpen, setOrderOpen] = useState(false) + const [explainOpen, setExplainOpen] = useState(false) + const [snapOpen, setSnapOpen] = useState(false) + const [presetOpen, setPresetOpen] = useState(false) + const [fixMsg, setFixMsg] = useState(null) + /** The built AI-fix prompt when the clipboard path failed — rendered as a + * selectable text block so the user can still copy it manually. */ + const [fixFallback, setFixFallback] = useState(null) + /** Bump to re-run the /dsh-market/check fetch after an order/snapshot-restore apply. */ + const [version, setVersion] = useState(0) + const refresh = useCallback(() => setVersion(v => v + 1), []) + + // --- issue #98 phase 2 (step 1): community-bundle ordering --------------- + /** Community bundle names from the report, in declared order. */ + const communityNames = useMemo( + () => report === null ? [] : report.bundles.filter(bundle => bundle.kind === 'community').map(bundle => bundle.name), + [report], + ) + /** Local editing state: re-synced whenever the report (re)loads. */ + const [order, setOrder] = useState(communityNames) + const [orderMsg, setOrderMsg] = useState(null) + const [orderErr, setOrderErr] = useState(null) + const [orderBusy, setOrderBusy] = useState(false) + /** Current-vs-candidate composition diff from a rejected static-composition + * validation (#125 review): what the candidate would change, shown as a hint. */ + const [orderDiff, setOrderDiff] = useState<{ overrides: number; orphans: number; duplicates: number } | null>(null) + /** + * Content identity of the last community order this draft synced to. A + * refresh() refetch returns a NEW array even when the order is unchanged, + * so a naive `setOrder(communityNames)` effect would wipe the user's + * in-progress drag/↑↓ edits on every unrelated re-check. Only resync when + * the report's community order actually CHANGED (apply order / snapshot + * restore) — an identical refetch keeps the draft (review M2). + */ + const syncedOrderRef = useRef(null) + useEffect(() => { + const synced = syncedOrderRef.current + const same = synced !== null + && synced.length === communityNames.length + && communityNames.every((name, i) => name === synced[i]) + if (same) return + syncedOrderRef.current = communityNames + setOrder(communityNames) + }, [communityNames]) + + /** Swap one community bundle with its neighbour (-1 up, +1 down). */ + const moveBundle = (index: number, delta: -1 | 1) => { + setOrder(prev => { + const next = [...prev] + const target = index + delta + if (target < 0 || target >= next.length) return prev + ;[next[index], next[target]] = [next[target]!, next[index]!] + return next + }) + } + + // --- drag & drop reordering (draft only — saved by 应用顺序 / Apply order) --- + /** Row being dragged (index into the local `order` draft). */ + const [dragIndex, setDragIndex] = useState(null) + /** Row currently under the pointer, highlighted as the drop target. */ + const [dragOverIndex, setDragOverIndex] = useState(null) + + const onRowDragStart = (index: number) => (event: DragEvent) => { + if (orderBusy) { + event.preventDefault() + return + } + setDragIndex(index) + event.dataTransfer?.setData?.('text/plain', order[index] ?? '') + if (event.dataTransfer !== undefined) event.dataTransfer.effectAllowed = 'move' + } + + const onRowDragOver = (index: number) => (event: DragEvent) => { + if (dragIndex === null || dragIndex === index) return + // preventDefault marks the row as a valid drop target (no auto-scroll). + event.preventDefault() + if (event.dataTransfer !== undefined) event.dataTransfer.dropEffect = 'move' + setDragOverIndex(index) + } + + const onRowDragLeave = (index: number) => () => { + setDragOverIndex(prev => prev === index ? null : prev) + } + + const onRowDrop = (index: number) => (event: DragEvent) => { + event.preventDefault() + const from = dragIndex + setDragIndex(null) + setDragOverIndex(null) + if (from === null || from === index) return + // Reorder the LOCAL draft only; the host is told via 应用顺序 / Apply order. + setOrder(prev => { + const next = [...prev] + const [moved] = next.splice(from, 1) + next.splice(index, 0, moved!) + return next + }) + } + + const onRowDragEnd = () => { + setDragIndex(null) + setDragOverIndex(null) + } + + /** POST the current community order; the host statically validates the + * candidate composition (dry-run replay) before writing. */ + const applyOrder = (target?: string[]) => { + if (orderBusy) return + setOrderBusy(true) + setOrderMsg(null) + setOrderErr(null) + setOrderDiff(null) + fetch(api('/dsh-market/bundle-order'), { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ order: target ?? order }), + }) + .then(async (res) => { + const body = (await res.json().catch(() => null)) as { + ok?: unknown + error?: unknown + trial?: { + errors?: Array<{ layer?: unknown; message?: unknown }> + warnings?: unknown[] + diff?: { overrides?: unknown[]; orphans?: unknown[]; duplicates?: unknown[] } + } + } | null + if (!res.ok || body?.ok !== true) { + // A rejected static-composition validation (422) carries the + // current-vs-candidate diff — surface what the candidate would + // change as an informational hint (issue #125 review). + const diff = body?.trial?.diff + const overrides = diff?.overrides?.length ?? 0 + const orphans = diff?.orphans?.length ?? 0 + const duplicates = diff?.duplicates?.length ?? 0 + setOrderDiff(overrides + orphans + duplicates > 0 ? { overrides, orphans, duplicates } : null) + const firstMessage = body?.trial?.errors?.[0]?.message + setOrderErr(body?.trial !== undefined + ? t('orderTrialFail').replace('{0}', firstMessage !== undefined ? String(firstMessage) : '') + : String(body?.error ?? `HTTP ${String(res.status)}`)) + return + } + setOrderDiff(null) + setOrderMsg(t('orderApplied')) + // Refetch the report so communityNames / the ordering draft reflect + // the applied order. + refresh() + }) + .catch((err: unknown) => setOrderErr(err instanceof Error ? err.message : String(err))) + .finally(() => setOrderBusy(false)) + } + + useEffect(() => { + let live = true + // Do NOT null the report here: a refresh() (manual re-check, or after an + // order/snapshot-restore apply) must keep the previous data visible and + // must not clobber the in-progress ordering draft, which re-syncs from + // communityNames only when the report actually changes (review M2). + setError(null) + fetch(api('/dsh-market/check'), { cache: 'no-store' }) + .then(async (res) => { + if (!res.ok) throw new Error(`HTTP ${String(res.status)}`) + const body = (await res.json()) as CheckReport + if (live) setReport(body) + }) + .catch((err: unknown) => { + if (live) setError(err instanceof Error ? err.message : String(err)) + }) + return () => { live = false } + }, [version]) + + if (error !== null) { + return
{t('checkLoadFail')}{error}
+ } + if (report === null) { + return ( +
+ + {t('checkLoading')} +
+ ) + } + + const summary = report.summary + const suggested = report.suggestedOrder ?? null + // #201: confirmed mismatches are tiered by the server-side classifyPeer + // verdict instead of the old binary `satisfied === false` split. A missing + // verdict (older host) keeps the legacy behavior: confirmed = warning tier. + const peerRisk = report.peerMismatches.filter(peer => peer.satisfied === false && peer.verdict?.kind === 'risk') + const peerWarning = report.peerMismatches.filter(peer => peer.satisfied === false && (peer.verdict === undefined || peer.verdict.kind === 'warning')) + // Classifier `none` (unparseable / * prerelease artifacts) and unevaluable + // rows share the informational display tier; true rows stay satisfied. + const peerInfo = report.peerMismatches.filter(peer => + (peer.satisfied === false && peer.verdict?.kind === 'none') || peer.satisfied === null) + const peerSatisfied = report.peerMismatches.filter(peer => peer.satisfied === true) + // Category counts for the overview strip: conflicts / peer tiers / order. + // Conflicts = HARD duplicate loader entries only; same-name rows are + // informational and stay out of the conflict count (review #109). + const catConflict = report.duplicates.length + const catRisk = peerRisk.length + const catWarn = peerWarning.length + const catInfo = peerInfo.length + const catMulti = report.multiVersion.length + const catOrder = report.orderConflicts?.length ?? 0 + const anyIssue = catConflict + catRisk + catWarn + catInfo + catMulti + catOrder > 0 + // AI-fix only shows for HARD issues — boot errors, duplicate entries, or a + // directional peer RISK. Optional peers, aboveMax without an explicit + // bound, and unknown rows are warning/info and never nudge an agent into + // risky changes without a clear problem (#201, conservative UX). + const hasHardIssues = summary.errors.length > 0 + || report.duplicates.length > 0 + || catRisk > 0 + + /** + * Build the AI-fix prompt (structured errors / duplicates / peer risks / + * order conflicts + scope) and copy it to the clipboard. The user pastes it + * into a new conversation and decides whether to send — the agent never runs + * automatically. + * (A previous auto-open/prefill attempt was dropped: it was unreliable + * across host versions, so plain copy + toast is the contract.) + */ + const startAgentFix = () => { + const lines: string[] = [] + lines.push(t('aiFixIntro').replace('{0}', report.profile)) + lines.push('') + // Self-identification guard FIRST: the prompt must make the receiving + // agent detect whether it is itself the harness running this profile. + // If it is, mutating the live composition — or upgrading/restarting the + // harness or core packages, or reinstalling deps — would kill the very + // session doing the fix, so those actions are forbidden and only a plan + // to run externally is produced. When the agent is NOT the target harness + // (a separate process), the conservative in-place scope below applies. + lines.push(t('aiFixDetect')) + lines.push('') + lines.push(t('aiFixIfSelf')) + lines.push('') + // Structured prompt only (#201): boot errors, duplicate loader ids and + // directional peer RISK rows. summary.warnings is no longer dumped — its + // peer lines contain the whole 30+ warning/info noise this issue untangles. + const errorLines = summary.errors.filter(line => !line.startsWith('duplicate loader entry id')) + if (errorLines.length > 0) { + lines.push(`${t('checkErrors')}:`) + for (const e of errorLines) lines.push(`- ${e}`) + lines.push('') + } + if (report.duplicates.length > 0) { + lines.push(`${t('checkDuplicates')}:`) + for (const dup of report.duplicates) lines.push(`- ${dup.id} × ${dup.count} (${dup.layers.join(' / ')})`) + lines.push('') + } + if (peerRisk.length > 0) { + lines.push(`${t('checkPeerRisk')}:`) + for (const peer of peerRisk) { + const direction = peer.verdict?.kind === 'risk' && peer.verdict.risk.direction === 'aboveMax' + ? t('peerRiskAboveMax') + : t('peerRiskBelowMin') + lines.push(`- ${peer.plugin} → ${peer.name}@${peer.range} (${t('checkResolved')}: ${peer.resolved ?? '—'}, ${direction})`) + } + lines.push('') + } + if ((report.orderConflicts ?? []).length > 0) { + lines.push(`${t('catOrder')}:`) + for (const c of report.orderConflicts ?? []) lines.push(`- ${c.name}: ${c.reason}`) + lines.push('') + } + lines.push(t('aiFixScope')) + lines.push('') + lines.push(t('aiFixConservative')) + const prompt = lines.join('\n') + + // Clipboard-first; on any failure (missing API or a rejected promise) show + // the prompt in a selectable block so the user can still copy it by hand — + // a bare "clipboard unavailable" message left nothing to copy. + setFixMsg(null) + setFixFallback(null) + const fallback = () => setFixFallback(prompt) + if (typeof navigator.clipboard?.writeText === 'function') { + navigator.clipboard.writeText(prompt) + .then(() => setFixMsg(t('aiFixCopied'))) + .catch(fallback) + } else { + fallback() + } + } + + return ( +
+
+ + + {summary.ok ? (anyIssue ? t('checkIssues') : t('diagOkAll')) : t('checkIssues')} + + + {t('catConflict')}: {catConflict} + + + {t('catRisk')}: {catRisk} + + + {t('catWarn')}: {catWarn} + + + {t('catInfo')}: {catInfo} + + + {t('checkMultiVersion')}: {catMulti} + + + {t('catOrder')}: {catOrder} + + + {hasHardIssues && ( + + )} + + {t('checkProfile')}: {report.profile} + {new Date(report.scannedAt).toLocaleString()} +
+ {fixMsg !== null &&
{fixMsg}
} + {fixFallback !== null && ( +
+

{t('aiFixFail')}

+