dshmarket 1.29.3 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -109,6 +109,10 @@ DSHM_REGISTRY_URL=https://your-mirror.example/plugins.json dsh web
109
109
 
110
110
  [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)
111
111
 
112
+ ### DSH App
113
+
114
+ [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.
115
+
112
116
  ### DSH Get
113
117
 
114
118
  [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).
package/README.zh.md CHANGED
@@ -106,6 +106,10 @@ DSHM_REGISTRY_URL=https://your-mirror.example/plugins.json dsh web
106
106
 
107
107
  [deepseek-harness-desktop](https://github.com/anywhere-labs/deepseek-harness-desktop)——基于 Electron 的 DeepSeek Harness 桌面客户端,理念是「万物皆插件,桌面本身也是插件」:支持 profile 切换、内置 Node 与 pnpm,安装前会先给 profile 拍快照以便回滚。[dshdesktop.cn](https://dshdesktop.cn)
108
108
 
109
+ ### DSH App
110
+
111
+ [dsh-app](https://github.com/RyensX/dsh-app)——基于 Tauri 2(而非 Electron)的 DeepSeek Harness 桌面客户端,因此安装包小得多,界面走系统 webview。AGPL-3.0。
112
+
109
113
  ### DSH Get
110
114
 
111
115
  [DSH Get](https://www.dshget.com/)——DeepSeek Harness 插件的网页检索目录:分类筛选、中英描述、安装命令与插件详情页;其规范化的目录快照公开在 [bobby-sheng/dshget-data](https://github.com/bobby-sheng/dshget-data)。
package/client/client.js CHANGED
@@ -267,6 +267,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
267
267
  restorePartial: "恢复已完成,但下列插件安装失败:",
268
268
  restoreUnportable: "依赖指向了另一台机器上的绝对路径,本机不存在,需要手动改成本机路径或重新安装",
269
269
  restoreBootError: "恢复后仍无法启动:",
270
+ orphanBundle: "⚠️ 下次启动会失败:profile 声明了这些 bundle,但它们没有安装。请重新安装,或从 profile 的 package.json 的 dsh.profile.bundles 里删掉:",
270
271
  restoreConfirm: "恢复将覆盖当前 profile 配置并重新安装插件,确定继续吗?",
271
272
  restorePreviewDone: "备份已导入,请在「已安装」中确认后开始恢复",
272
273
  restoreMissing: "备份中有 {0} 个插件尚未安装",
@@ -712,6 +713,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
712
713
  restorePartial: "Restore completed, but these plugins failed to install:",
713
714
  restoreUnportable: "points at an absolute path from another machine that does not exist here — repoint it locally or reinstall the plugin",
714
715
  restoreBootError: "Still cannot boot after the restore:",
716
+ 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:",
715
717
  restoreConfirm: "Restore will overwrite this profile configuration and reinstall plugins. Continue?",
716
718
  restorePreviewDone: "Backup imported. Review Installed, then start restore.",
717
719
  restoreMissing: "{0} plugins from this backup are not installed",
@@ -5067,6 +5069,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
5067
5069
  */
5068
5070
  const [records, setRecords] = (0, react.useState)([]);
5069
5071
  const recordSeq = (0, react.useRef)(0);
5072
+ /** The synthetic install task rebuilt from dshm-pending after a remount. */
5073
+ const recoveredInstall = (0, react.useRef)(null);
5074
+ /** The synthetic task rebuilt from dshm-updating after this section remounts. */
5075
+ const recoveredUpdateRecordId = (0, react.useRef)(null);
5070
5076
  /** Raised by the card marker, so "查看详情" lands on the record itself. */
5071
5077
  const [operationsOpen, setOperationsOpen] = (0, react.useState)(false);
5072
5078
  const openOperations = (0, react.useCallback)(() => setOperationsOpen(true), []);
@@ -5414,10 +5420,41 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
5414
5420
  }, []);
5415
5421
  (0, react.useEffect)(() => {
5416
5422
  const pending = readSession("dshm-pending");
5417
- if (pending !== null && typeof pending.url === "string") setBusyUrl(pending.url);
5423
+ if (pending !== null && typeof pending.url === "string") {
5424
+ setBusyUrl(pending.url);
5425
+ recoveredInstall.current = {
5426
+ id: `recovered-install:${pending.url}`,
5427
+ url: pending.url,
5428
+ ...typeof pending.name === "string" && pending.name !== "" ? { name: pending.name } : {}
5429
+ };
5430
+ }
5418
5431
  const updating = readSession("dshm-updating");
5419
- if (updating !== null && typeof updating.name === "string" && updating.name !== "") setUpdatingName(updating.name);
5432
+ if (updating !== null && typeof updating.name === "string" && updating.name !== "") {
5433
+ setUpdatingName(updating.name);
5434
+ const id = `recovered-update:${updating.name}`;
5435
+ recoveredUpdateRecordId.current = id;
5436
+ setRecords((list) => list.some((record) => record.kind === "update" && record.name === updating.name && record.state === "running") ? list : enqueue(list, {
5437
+ id,
5438
+ kind: "update",
5439
+ name: updating.name,
5440
+ state: "running"
5441
+ }));
5442
+ }
5420
5443
  }, []);
5444
+ (0, react.useEffect)(() => {
5445
+ const recovered = recoveredInstall.current;
5446
+ if (recovered === null) return;
5447
+ const name = recovered.name ?? data?.plugins.find((plugin) => plugin.url === recovered.url)?.name;
5448
+ if (name === void 0) return;
5449
+ recovered.name = name;
5450
+ setRecords((list) => list.some((record) => record.id === recovered.id) ? list : enqueue(list, {
5451
+ id: recovered.id,
5452
+ kind: "install",
5453
+ name,
5454
+ url: recovered.url,
5455
+ state: "running"
5456
+ }));
5457
+ }, [data]);
5421
5458
  (0, react.useEffect)(() => {
5422
5459
  if (busyUrl === null && updatingName === null) {
5423
5460
  setProgressLine(null);
@@ -5462,11 +5499,21 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
5462
5499
  if (data !== null && data.plugins.some((p) => p.url === busyUrl && isInstalled(p, statusInstalled, repoIdentities, data.plugins, repoHints))) {
5463
5500
  idleStrikes.current = 0;
5464
5501
  sessionStorage.removeItem("dshm-pending");
5502
+ const recovered = recoveredInstall.current;
5503
+ if (recovered !== null) {
5504
+ setRecords((list) => drop(list, recovered.id));
5505
+ recoveredInstall.current = null;
5506
+ }
5465
5507
  setDoneUrls((urls) => urls.includes(busyUrl) ? urls : urls.concat(busyUrl));
5466
5508
  setBusyUrl(null);
5467
5509
  } else if (++idleStrikes.current >= 2) {
5468
5510
  idleStrikes.current = 0;
5469
5511
  sessionStorage.removeItem("dshm-pending");
5512
+ const recovered = recoveredInstall.current;
5513
+ if (recovered !== null) {
5514
+ setRecords((list) => drop(list, recovered.id));
5515
+ recoveredInstall.current = null;
5516
+ }
5470
5517
  setBusyUrl(null);
5471
5518
  setInstallError(t("installFail") + " — " + t("exportLog"));
5472
5519
  }
@@ -5475,6 +5522,11 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
5475
5522
  if (++updateIdleStrikes.current >= 2) {
5476
5523
  updateIdleStrikes.current = 0;
5477
5524
  sessionStorage.removeItem("dshm-updating");
5525
+ const recoveredId = recoveredUpdateRecordId.current;
5526
+ if (recoveredId !== null) {
5527
+ setRecords((list) => drop(list, recoveredId));
5528
+ recoveredUpdateRecordId.current = null;
5529
+ }
5478
5530
  setUpdatingName(null);
5479
5531
  refreshInstalled();
5480
5532
  }
@@ -5611,7 +5663,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
5611
5663
  url: plugin.url,
5612
5664
  state: "running"
5613
5665
  }));
5614
- sessionStorage.setItem("dshm-pending", JSON.stringify({ url: plugin.url }));
5666
+ sessionStorage.setItem("dshm-pending", JSON.stringify({
5667
+ url: plugin.url,
5668
+ name: plugin.name
5669
+ }));
5615
5670
  fetch("/dsh-market/install", {
5616
5671
  method: "POST",
5617
5672
  headers: { "content-type": "application/json" },
@@ -5685,7 +5740,9 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
5685
5740
  names: blocked
5686
5741
  });
5687
5742
  const text = (v) => typeof v === "string" ? v : v && typeof v.text === "string" ? v.text : v == null ? "" : JSON.stringify(v);
5688
- const detail = text(body.error) || humanOutput([text(body.stderr), text(body.stdout)].filter(Boolean).join("\n")) || "exit " + body.exitCode;
5743
+ const orphans = Array.isArray(body.orphanBundles) ? body.orphanBundles.map(String) : [];
5744
+ const failure = text(body.error) || humanOutput([text(body.stderr), text(body.stdout)].filter(Boolean).join("\n")) || "exit " + body.exitCode;
5745
+ const detail = orphans.length > 0 ? `${t("orphanBundle")} ${orphans.join(", ")}\n${failure}` : failure;
5689
5746
  setRecords((list) => patch(list, recordId, {
5690
5747
  state: "failed",
5691
5748
  reason: detail.trim().slice(-600),
@@ -5903,7 +5960,9 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
5903
5960
  restore
5904
5961
  });
5905
5962
  const text = (v) => typeof v === "string" ? v : v && typeof v.text === "string" ? v.text : v == null ? "" : JSON.stringify(v);
5906
- const detail = text(body.error) || humanOutput([text(body.stderr), text(body.stdout)].filter(Boolean).join("\n")) || "exit " + body.exitCode;
5963
+ const orphans = Array.isArray(body.orphanBundles) ? body.orphanBundles.map(String) : [];
5964
+ const failure = text(body.error) || humanOutput([text(body.stderr), text(body.stdout)].filter(Boolean).join("\n")) || "exit " + body.exitCode;
5965
+ const detail = orphans.length > 0 ? `${t("orphanBundle")} ${orphans.join(", ")}\n${failure}` : failure;
5907
5966
  setRecords((list) => patch(list, updateRecordId, {
5908
5967
  state: "failed",
5909
5968
  reason: detail.trim().slice(-600)