dshmarket 1.66.23 → 1.66.25

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/client/client.js CHANGED
@@ -193,7 +193,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
193
193
  const IconRefreshOutline14 = pickIcon("IconRefreshOutlineRegular", "IconRefreshOutline14");
194
194
  const IconSearchOutline16 = pickIcon("IconSearchOutlineRegular", "IconSearchOutline16");
195
195
  const IconSparkle16 = pickIcon("IconSparkleRegular", "IconSparkle16");
196
- const IconWarningOutline16 = pickIcon("IconWarningOutlineRegular", "IconWarningOutline16");
196
+ const IconWarningOutline16$1 = pickIcon("IconWarningOutlineRegular", "IconWarningOutline16");
197
197
  //#endregion
198
198
  //#region src/client/locales.ts
199
199
  /** zh/en dictionaries for the Market settings section and install toast. */
@@ -383,6 +383,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
383
383
  updateNow: "立即更新",
384
384
  updateFail: "更新失败",
385
385
  upToDate: "已是最新",
386
+ newerPrereleaseAvailable: "预发布版 {0} 已发布",
387
+ newerPrereleaseHint: "npm 上的 latest 没有更新;你在跟的这条预发布线发布了更新的版本。",
386
388
  unreleasedCommits: "有新提交(未发版){0}",
387
389
  unreleasedCommitsHint: "所选分支上有比你已装版本更新的提交,但没有发布新版本号;市场只安装正式版本,不会自动装这些提交。",
388
390
  linkedDev: "本地开发",
@@ -395,6 +397,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
395
397
  notesNpm: "版本发布时间",
396
398
  notesNone: "该插件未提供更新说明。",
397
399
  notesSource: "市场还没收录这个插件的更新说明。",
400
+ notesSourceUnreadable: "这个插件不是从 GitHub 装的,市场读不到它的改动记录。",
398
401
  notesSourceLink: "去仓库看改了什么",
399
402
  notesSourceNoLink: "可以问问插件作者改了什么。",
400
403
  notesLoadFail: "暂时读不到更新说明,稍后再试。",
@@ -589,6 +592,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
589
592
  webdavRestore: "从 WebDAV 恢复",
590
593
  autoBackup: "每天自动备份(打开市场时)",
591
594
  webdavNote: "WebDAV 地址与用户名仅保存在当前浏览器;密码只存于服务端,每次会话需重新输入。",
595
+ webdavPathHint: "填完整的文件地址(含文件名):备份写进这个文件,恢复也从它读。开启自动备份后每天最多上传一次。",
592
596
  localOnly: "WebDAV 地址和用户名只保存在此浏览器。",
593
597
  credsWarning: "请注意:备份包含 profile 配置,以及可能含密钥的文件(config.toml、.env 等)。本地导出的文件会原样包含这些内容,上传 WebDAV 前请确认目标可信。",
594
598
  gist: "GitHub Gist",
@@ -913,7 +917,14 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
913
917
  recoveryRestarting: "正在重启:dsh 启动后本页会刷新;若又失败,会带着新的报错回到这里。",
914
918
  recoveryApplyFailed: "保存失败:",
915
919
  recoveryWriteFailed: "上次的选择没有全部保存,因此没有重启:",
916
- recoveryTimeout: "等待 DeepSeek Harness 启动超时。可点「调整插件」,检查是否某个插件导致无法启动。"
920
+ recoveryTimeout: "等待 DeepSeek Harness 启动超时。可点「调整插件」,检查是否某个插件导致无法启动。",
921
+ hostRecoveryTitle: "配置被换成了空壳",
922
+ hostRecoveryBody: "上次启动失败后,DSH 的恢复流程把配置换成了默认的。原来的 {0} 条还在备份里。",
923
+ hostRecoverySource: "备份:{0},{1}",
924
+ hostRecoveryRestore: "恢复配置",
925
+ hostRecoveryRestored: "已恢复 {0} 条设置。刷新页面就能看到。",
926
+ hostRecoveryFail: "恢复失败:",
927
+ hostRecoveryNote: "恢复前会自动存快照,随时能退回。"
917
928
  };
918
929
  const en = {
919
930
  nav: "Plugin Market",
@@ -1100,6 +1111,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1100
1111
  updateNow: "Update now",
1101
1112
  updateFail: "Update failed",
1102
1113
  upToDate: "Up to date",
1114
+ newerPrereleaseAvailable: "prerelease {0} published",
1115
+ newerPrereleaseHint: "The package's latest tag has no update; the prerelease line you are on published a newer build.",
1103
1116
  unreleasedCommits: "New commits (unreleased) {0}",
1104
1117
  unreleasedCommitsHint: "The selected branch has commits newer than your install, but no new version was published; the market installs releases only and will not apply them automatically.",
1105
1118
  linkedDev: "local",
@@ -1112,6 +1125,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1112
1125
  notesNpm: "Version publish times",
1113
1126
  notesNone: "This plugin does not publish update notes.",
1114
1127
  notesSource: "The market doesn't have update notes for this plugin yet.",
1128
+ notesSourceUnreadable: "This plugin was not installed from GitHub, so the market cannot read its change history.",
1115
1129
  notesSourceLink: "See what changed in the repository",
1116
1130
  notesSourceNoLink: "You can ask the plugin author what changed.",
1117
1131
  notesLoadFail: "Update notes are unavailable right now — try again later.",
@@ -1306,6 +1320,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1306
1320
  webdavRestore: "Restore from WebDAV",
1307
1321
  autoBackup: "Back up daily (when the market opens)",
1308
1322
  webdavNote: "The WebDAV URL and username stay in this browser only; the password is kept server-side and must be re-entered each session.",
1323
+ webdavPathHint: "Give the whole file path, name included: the backup writes that file and restore reads it back. With automatic backup on it uploads at most once a day.",
1309
1324
  localOnly: "The WebDAV URL and username stay in this browser only.",
1310
1325
  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.",
1311
1326
  gist: "GitHub Gist",
@@ -1630,7 +1645,14 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1630
1645
  recoveryRestarting: "Restarting: this page reloads once dsh is up; if it fails again you come back here with the new error.",
1631
1646
  recoveryApplyFailed: "Could not save: ",
1632
1647
  recoveryWriteFailed: "The last choice was not saved in full, so nothing was restarted: ",
1633
- recoveryTimeout: "Timed out waiting for DeepSeek Harness to start. \"Adjust plugins\" can tell you whether one of them is what stops it."
1648
+ recoveryTimeout: "Timed out waiting for DeepSeek Harness to start. \"Adjust plugins\" can tell you whether one of them is what stops it.",
1649
+ hostRecoveryTitle: "Your configuration was replaced",
1650
+ hostRecoveryBody: "After the last failed start, DSH's recovery replaced your configuration with a default one. The previous {0} settings are still in the backup.",
1651
+ hostRecoverySource: "Backup: {0}, {1}",
1652
+ hostRecoveryRestore: "Restore configuration",
1653
+ hostRecoveryRestored: "Restored {0} settings. Refresh the page to see them.",
1654
+ hostRecoveryFail: "Restore failed: ",
1655
+ hostRecoveryNote: "A snapshot is taken first, so you can roll this back."
1634
1656
  };
1635
1657
  //#endregion
1636
1658
  //#region src/catalog-local-match.ts
@@ -4121,7 +4143,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
4121
4143
  size: 13,
4122
4144
  className: Market_module_css_default.reassureOk
4123
4145
  });
4124
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
4146
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, {
4125
4147
  size: 14,
4126
4148
  className: Market_module_css_default.conflictIcon
4127
4149
  });
@@ -4503,14 +4525,14 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
4503
4525
  children: [
4504
4526
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4505
4527
  className: Market_module_css_default.recoverySummary,
4506
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
4528
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, {
4507
4529
  size: 14,
4508
4530
  className: Market_module_css_default.bannerIcon
4509
4531
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: view.failure.summary || t("recoveryNoSummary") })]
4510
4532
  }),
4511
4533
  view.lastErrors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4512
4534
  className: Market_module_css_default.recoverySummary,
4513
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
4535
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, {
4514
4536
  size: 14,
4515
4537
  className: Market_module_css_default.bannerIcon
4516
4538
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("recoveryWriteFailed"), view.lastErrors.join("; ")] })]
@@ -4578,6 +4600,156 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
4578
4600
  });
4579
4601
  }
4580
4602
  //#endregion
4603
+ //#region src/client/HostRecoveryBanner.tsx
4604
+ /**
4605
+ * #875: the way back from DSH's own fatal recovery.
4606
+ *
4607
+ * `sanitizeProfile` renames the profile patch to a `.bak-<epoch ms>` sibling
4608
+ * and writes a stub in its place, then boots normally — 22 KB of providers,
4609
+ * models and permission presets disappeared that way with nothing on screen
4610
+ * saying so. The server decides whether the shape is really the wipe
4611
+ * (`GET /dsh-market/host-recovery`); this banner is what the user sees when it
4612
+ * is, and it only appears then. Nothing else on the page changes, because for
4613
+ * every other profile this is not a thing that happened.
4614
+ *
4615
+ * Restoring is a single click, deliberately: it is undoable (the server
4616
+ * snapshots first and keeps the current file under a new `.bak` name), and a
4617
+ * confirmation here would be asking the user to judge something they cannot
4618
+ * see — the two patches side by side.
4619
+ */
4620
+ function hostRecoveryOf(value) {
4621
+ if (value === null || typeof value !== "object") return null;
4622
+ const view = value;
4623
+ if (typeof view.offerRestore !== "boolean" || !Array.isArray(view.backups) || !Array.isArray(view.lostRowIds)) return null;
4624
+ const backups = [];
4625
+ for (const entry of view.backups) {
4626
+ if (entry === null || typeof entry !== "object") continue;
4627
+ const item = entry;
4628
+ if (typeof item.name !== "string" || typeof item.bytes !== "number") continue;
4629
+ backups.push({
4630
+ name: item.name,
4631
+ stamp: typeof item.stamp === "number" ? item.stamp : null,
4632
+ bytes: item.bytes,
4633
+ rowIds: Array.isArray(item.rowIds) ? item.rowIds.filter((id) => typeof id === "string") : []
4634
+ });
4635
+ }
4636
+ return {
4637
+ patchName: typeof view.patchName === "string" ? view.patchName : "cordis.patch.yml",
4638
+ currentBytes: typeof view.currentBytes === "number" ? view.currentBytes : 0,
4639
+ backups,
4640
+ lostRowIds: view.lostRowIds.filter((id) => typeof id === "string"),
4641
+ offerRestore: view.offerRestore
4642
+ };
4643
+ }
4644
+ /** "22.7 KB" / "1.2 MB" — the size is what makes the backup believable. */
4645
+ function formatBytes(bytes) {
4646
+ if (bytes >= 1048576) return `${(bytes / 1048576).toFixed(1)} MB`;
4647
+ if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
4648
+ return `${bytes} B`;
4649
+ }
4650
+ function HostRecoveryBanner(props) {
4651
+ const { t, bootId } = props;
4652
+ const [view, setView] = (0, react.useState)(null);
4653
+ const [busy, setBusy] = (0, react.useState)(false);
4654
+ const [done, setDone] = (0, react.useState)(null);
4655
+ const [error, setError] = (0, react.useState)(null);
4656
+ const [dismissed, setDismissed] = (0, react.useState)(false);
4657
+ (0, react.useEffect)(() => {
4658
+ try {
4659
+ setDismissed(sessionStorage.getItem("dshm-host-recovery-dismissed") === String(bootId ?? ""));
4660
+ } catch {}
4661
+ }, [bootId]);
4662
+ const dismiss = (0, react.useCallback)(() => {
4663
+ setDismissed(true);
4664
+ try {
4665
+ sessionStorage.setItem("dshm-host-recovery-dismissed", String(bootId ?? ""));
4666
+ } catch {}
4667
+ }, [bootId]);
4668
+ (0, react.useEffect)(() => {
4669
+ let live = true;
4670
+ fetch(api("/dsh-market/host-recovery"), { cache: "no-store" }).then((res) => res.json()).then((body) => {
4671
+ if (live) setView(hostRecoveryOf(body));
4672
+ }).catch(() => {});
4673
+ return () => {
4674
+ live = false;
4675
+ };
4676
+ }, [bootId]);
4677
+ const restore = (0, react.useCallback)(() => {
4678
+ const newest = view?.backups[0];
4679
+ if (newest === void 0 || busy) return;
4680
+ setBusy(true);
4681
+ setError(null);
4682
+ fetch(api("/dsh-market/host-recovery/restore"), {
4683
+ method: "POST",
4684
+ headers: { "content-type": "application/json" },
4685
+ body: JSON.stringify({ name: newest.name })
4686
+ }).then(async (res) => ({
4687
+ ok: res.ok,
4688
+ body: await res.json().catch(() => null)
4689
+ })).then(({ ok, body }) => {
4690
+ if (ok) setDone(typeof body?.restored?.rows === "number" ? body.restored.rows : 0);
4691
+ else setError(body?.error ?? "");
4692
+ }).catch(() => setError("network")).finally(() => setBusy(false));
4693
+ }, [busy, view]);
4694
+ if (dismissed) return null;
4695
+ if (done !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4696
+ className: Market_module_css_default.banner,
4697
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4698
+ className: Market_module_css_default.grow,
4699
+ children: t("hostRecoveryRestored").replace("{0}", String(done))
4700
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4701
+ variant: "primary",
4702
+ size: "sm",
4703
+ onClick: () => location.reload(),
4704
+ children: t("refresh")
4705
+ })]
4706
+ });
4707
+ if (view === null || !view.offerRestore) return null;
4708
+ const newest = view.backups[0];
4709
+ const when = newest?.stamp != null ? new Date(newest.stamp).toLocaleString() : "";
4710
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4711
+ className: Market_module_css_default.banner,
4712
+ children: [
4713
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {
4714
+ size: 14,
4715
+ className: Market_module_css_default.bannerIcon
4716
+ }),
4717
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4718
+ className: Market_module_css_default.grow,
4719
+ children: [
4720
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: t("hostRecoveryTitle") }) }),
4721
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("hostRecoveryBody").replace("{0}", String(view.lostRowIds.length)) }),
4722
+ newest !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4723
+ className: Market_module_css_default.spec,
4724
+ children: [
4725
+ t("hostRecoverySource").replace("{0}", formatBytes(newest.bytes)).replace("{1}", when),
4726
+ " — ",
4727
+ t("hostRecoveryNote")
4728
+ ]
4729
+ }),
4730
+ error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4731
+ className: Market_module_css_default.spec,
4732
+ children: [t("hostRecoveryFail"), error]
4733
+ })
4734
+ ]
4735
+ }),
4736
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4737
+ variant: "primary",
4738
+ size: "sm",
4739
+ disabled: busy || newest === void 0,
4740
+ onClick: restore,
4741
+ children: busy ? t("backupWorking") : t("hostRecoveryRestore")
4742
+ }),
4743
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4744
+ variant: "ghost",
4745
+ size: "sm",
4746
+ onClick: dismiss,
4747
+ children: t("dismiss")
4748
+ })
4749
+ ]
4750
+ });
4751
+ }
4752
+ //#endregion
4581
4753
  //#region src/client/preset-panel.tsx
4582
4754
  /**
4583
4755
  * Diagnostics sub-panel — issue #98 phase 3 (plugin presets).
@@ -6458,7 +6630,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
6458
6630
  const remaining = findings.length - preview.length;
6459
6631
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6460
6632
  className: Market_module_css_default.banner,
6461
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
6633
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, {
6462
6634
  size: 14,
6463
6635
  className: Market_module_css_default.bannerIcon
6464
6636
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
@@ -8591,6 +8763,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
8591
8763
  const [restarting, setRestarting] = (0, react.useState)(false);
8592
8764
  const [showTop, setShowTop] = (0, react.useState)(false);
8593
8765
  const [backupBusy, setBackupBusy] = (0, react.useState)(false);
8766
+ const webdavInFlight = (0, react.useRef)(false);
8594
8767
  const [backupMessage, setBackupMessage] = (0, react.useState)(null);
8595
8768
  const [backupRestored, setBackupRestored] = (0, react.useState)(false);
8596
8769
  const [pendingBackup, setPendingBackup] = (0, react.useState)(null);
@@ -10743,7 +10916,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
10743
10916
  }).catch((error) => setBackupMessage(String(error))).finally(() => setBackupBusy(false));
10744
10917
  }, [finishRestore, pendingBackup]);
10745
10918
  const runWebdav = (0, react.useCallback)((action) => {
10746
- if (webdavUrl.trim() === "") return;
10919
+ if (webdavUrl.trim() === "" || webdavInFlight.current) return;
10920
+ webdavInFlight.current = true;
10747
10921
  setBackupBusy(true);
10748
10922
  setBackupMessage(null);
10749
10923
  setRestoreErrors([]);
@@ -10766,7 +10940,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
10766
10940
  } catch {}
10767
10941
  setBackupMessage(t("backupDone"));
10768
10942
  }
10769
- }).catch((error) => setBackupMessage(String(error))).finally(() => setBackupBusy(false));
10943
+ }).catch((error) => setBackupMessage(String(error))).finally(() => {
10944
+ webdavInFlight.current = false;
10945
+ setBackupBusy(false);
10946
+ });
10770
10947
  }, [
10771
10948
  previewBackup,
10772
10949
  t,
@@ -10890,6 +11067,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
10890
11067
  setExportIncludeConfig(false);
10891
11068
  setExportOpen(true);
10892
11069
  }, [installed, installedBundles]);
11070
+ const runWebdavRef = (0, react.useRef)(runWebdav);
11071
+ (0, react.useEffect)(() => {
11072
+ runWebdavRef.current = runWebdav;
11073
+ }, [runWebdav]);
10893
11074
  (0, react.useEffect)(() => {
10894
11075
  try {
10895
11076
  localStorage.setItem(WEBDAV_STORAGE_KEY, JSON.stringify({
@@ -10898,18 +11079,19 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
10898
11079
  auto: autoBackup
10899
11080
  }));
10900
11081
  } catch {}
10901
- if (!autoBackup || webdavUrl.trim() === "") return;
10902
- let last = 0;
10903
- try {
10904
- last = Number(localStorage.getItem("dshm-webdav-last")) || 0;
10905
- } catch {}
10906
- if (Date.now() - last >= 864e5) runWebdav("backup");
10907
11082
  }, [
10908
11083
  autoBackup,
10909
- runWebdav,
10910
11084
  webdavUrl,
10911
11085
  webdavUser
10912
11086
  ]);
11087
+ (0, react.useEffect)(() => {
11088
+ if (!autoBackup) return;
11089
+ let last = 0;
11090
+ try {
11091
+ last = Number(localStorage.getItem("dshm-webdav-last")) || 0;
11092
+ } catch {}
11093
+ if (Date.now() - last >= 864e5) runWebdavRef.current("backup");
11094
+ }, [autoBackup]);
10913
11095
  const sessionPendingRestart = doneUrls.length + restartNames.length + removedCount + toggleRestart + (backupRestored ? 1 : 0);
10914
11096
  /**
10915
11097
  * Plugins the HOST reports as restart-pending, independent of what this
@@ -11196,7 +11378,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
11196
11378
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
11197
11379
  redLines.filter(redLineIsUrgent).map((line) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
11198
11380
  className: Market_module_css_default.warnLine,
11199
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
11381
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, {
11200
11382
  size: 14,
11201
11383
  className: Market_module_css_default.bannerIcon
11202
11384
  }), " " + redLineLabel(line)]
@@ -11395,12 +11577,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
11395
11577
  type: "button",
11396
11578
  className: Market_module_css_default.cardBlockedMark,
11397
11579
  onClick: openOperations,
11398
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t("queuedBadge")]
11580
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, { size: 13 }), t("queuedBadge")]
11399
11581
  }) : blocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
11400
11582
  type: "button",
11401
11583
  className: Market_module_css_default.cardBlockedMark,
11402
11584
  onClick: openOperations,
11403
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t("opBlockedCard")]
11585
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, { size: 13 }), t("opBlockedCard")]
11404
11586
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
11405
11587
  variant: "primary",
11406
11588
  size: "sm",
@@ -11617,12 +11799,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
11617
11799
  type: "button",
11618
11800
  className: Market_module_css_default.cardBlockedMark,
11619
11801
  onClick: openOperations,
11620
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t("queuedBadge")]
11802
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, { size: 13 }), t("queuedBadge")]
11621
11803
  }) : blocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
11622
11804
  type: "button",
11623
11805
  className: Market_module_css_default.cardBlockedMark,
11624
11806
  onClick: openOperations,
11625
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t("opBlockedCard")]
11807
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, { size: 13 }), t("opBlockedCard")]
11626
11808
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
11627
11809
  variant: "primary",
11628
11810
  size: "sm",
@@ -12072,7 +12254,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
12072
12254
  }),
12073
12255
  restoreErrors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12074
12256
  className: Market_module_css_default.banner,
12075
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
12257
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, {
12076
12258
  size: 14,
12077
12259
  className: Market_module_css_default.bannerIcon
12078
12260
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
@@ -12083,6 +12265,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
12083
12265
  }, error))]
12084
12266
  })]
12085
12267
  }),
12268
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HostRecoveryBanner, {
12269
+ t,
12270
+ bootId
12271
+ }),
12086
12272
  tab === "installed" && pendingBackup !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12087
12273
  className: Market_module_css_default.banner,
12088
12274
  children: [
@@ -12180,7 +12366,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
12180
12366
  }),
12181
12367
  activationWarnings.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12182
12368
  className: Market_module_css_default.banner,
12183
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
12369
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, {
12184
12370
  size: 14,
12185
12371
  className: Market_module_css_default.bannerIcon
12186
12372
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -12202,7 +12388,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
12202
12388
  }),
12203
12389
  tab === "installed" && configNamespaceIssues.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12204
12390
  className: Market_module_css_default.banner,
12205
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
12391
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, {
12206
12392
  size: 14,
12207
12393
  className: Market_module_css_default.bannerIcon
12208
12394
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -12255,7 +12441,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
12255
12441
  buildsSkipped !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
12256
12442
  className: Market_module_css_default.banner,
12257
12443
  children: [
12258
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
12444
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, {
12259
12445
  size: 14,
12260
12446
  className: Market_module_css_default.bannerIcon
12261
12447
  }),
@@ -12460,6 +12646,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
12460
12646
  placeholder: t("webdavUrl"),
12461
12647
  onChange: (e) => setWebdavUrl(e.target.value)
12462
12648
  }),
12649
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("webdavPathHint") }),
12463
12650
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
12464
12651
  className: Market_module_css_default.backupInput,
12465
12652
  autoComplete: "username",
@@ -13641,7 +13828,17 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
13641
13828
  className: Market_module_css_default.metaTag,
13642
13829
  title: t("unreleasedCommitsHint"),
13643
13830
  children: t("unreleasedCommits").replace("{0}", displayLatest(status.unreleasedCommit))
13644
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13831
+ }) : status?.newerPrerelease !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13832
+ className: Market_module_css_default.metaTag,
13833
+ title: t("newerPrereleaseHint"),
13834
+ children: t("newerPrereleaseAvailable").replace("{0}", status.newerPrerelease)
13835
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
13836
+ variant: "outline",
13837
+ size: "sm",
13838
+ disabled: updatingName !== null || removingName !== null || busyUrl !== null,
13839
+ onClick: () => doUpdate(name, false, false, status.newerPrerelease),
13840
+ children: t("install")
13841
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13645
13842
  className: Market_module_css_default.metaTag,
13646
13843
  title: t("upToDate"),
13647
13844
  children: t("upToDate")
@@ -14058,7 +14255,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
14058
14255
  ]
14059
14256
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
14060
14257
  className: Market_module_css_default.migrationWarning,
14061
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }), t("migrateWarning")]
14258
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, { size: 14 }), t("migrateWarning")]
14062
14259
  })]
14063
14260
  }),
14064
14261
  agentsConfirm !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
@@ -14283,7 +14480,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
14283
14480
  })]
14284
14481
  }) : updateNotes.kind === "source" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14285
14482
  className: Market_module_css_default.spec,
14286
- children: [t(updateNotes.reason === "unavailable" ? "notesLoadFail" : "notesSource"), typeof updateNotes.sourceUrl === "string" && /^https?:\/\//i.test(updateNotes.sourceUrl) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [lang === "zh" ? "" : " ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
14483
+ children: [t(updateNotes.reason === "unavailable" ? "notesLoadFail" : updateNotes.reason === "unreadable" ? "notesSourceUnreadable" : "notesSource"), typeof updateNotes.sourceUrl === "string" && /^https?:\/\//i.test(updateNotes.sourceUrl) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [lang === "zh" ? "" : " ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
14287
14484
  className: Market_module_css_default.notesSourceLink,
14288
14485
  href: updateNotes.sourceUrl,
14289
14486
  target: "_blank",
@@ -14399,17 +14596,17 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
14399
14596
  }),
14400
14597
  exportState === "fail" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
14401
14598
  text: t("exportLogFail"),
14402
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }),
14599
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, { size: 14 }),
14403
14600
  onDone: exportToastDone
14404
14601
  }),
14405
14602
  favoriteError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
14406
14603
  text: localizeBilingual(favoriteError, lang),
14407
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }),
14604
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, { size: 14 }),
14408
14605
  onDone: favoriteErrorDone
14409
14606
  }),
14410
14607
  blockError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
14411
14608
  text: localizeBilingual(blockError, lang),
14412
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }),
14609
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, { size: 14 }),
14413
14610
  onDone: blockErrorDone
14414
14611
  }),
14415
14612
  blockNotice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
@@ -14418,7 +14615,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
14418
14615
  }),
14419
14616
  updateExemptError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
14420
14617
  text: localizeBilingual(updateExemptError, lang),
14421
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }),
14618
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, { size: 14 }),
14422
14619
  onDone: updateExemptErrorDone
14423
14620
  }),
14424
14621
  updateExemptNotice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
@@ -14427,7 +14624,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
14427
14624
  }),
14428
14625
  toggled !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
14429
14626
  text: toggled.name + " " + t(toggled.enabled ? "toastToggledOn" : "toastToggledOff"),
14430
- icon: toggled.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCheckOutline16, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }),
14627
+ icon: toggled.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCheckOutline16, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16$1, { size: 14 }),
14431
14628
  onDone: toggledDone
14432
14629
  })
14433
14630
  ]
@@ -15216,6 +15413,17 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
15216
15413
  }
15217
15414
  const iconGaps = missingIcons(mod);
15218
15415
  if (iconGaps.length > 0) console.warn("[dsh-market] host ui-primitives missing icons " + iconGaps.join(", ") + " — rendering without them");
15416
+ try {
15417
+ mountMarket(ctx);
15418
+ } catch (error) {
15419
+ console.error("[dsh-market] mounting failed, so the market is missing for this session. The rest of the app is unaffected. Report at https://github.com/dsh-market/dsh-market/issues with this line and the stack below:", error);
15420
+ }
15421
+ }
15422
+ /**
15423
+ * The market's mounts, split out only so apply() can contain a throw from any
15424
+ * of them. Not exported: the split is a guard, not a surface.
15425
+ */
15426
+ function mountMarket(ctx) {
15219
15427
  ctx.effect(() => ctx.locale.register(NS, {
15220
15428
  zh,
15221
15429
  en
package/lib/changelog.js CHANGED
@@ -341,9 +341,17 @@ export async function updateNotesFor(profile, explicitDir, name) {
341
341
  const specKey = repoKeyOf(spec) ?? lookupRepoFromUrl(spec);
342
342
  // The market holds no notes for this install, but its history is still
343
343
  // readable where it came from: say which, and point there.
344
+ //
345
+ // Notes come from the catalog's own release feed, which is built from
346
+ // GitHub, so a gitee or self-hosted git install has no feed to find:
347
+ // "the market has not collected this one yet" would promise something that
348
+ // cannot happen, and the reader waits for it (#819). Those say the source
349
+ // cannot be read instead. The url is the same either way — the repository
350
+ // is where the answer actually is.
351
+ const unreadableSource = githubIdentityOf(spec) === null && isGitHostedSpec(spec);
344
352
  const fromSource = (reason) => ({
345
353
  kind: 'source',
346
- reason,
354
+ reason: unreadableSource ? 'unreadable' : reason,
347
355
  sourceUrl: sourceWebUrl(spec) ?? specKey,
348
356
  });
349
357
  try {