dsh-archived-chats 1.7.0 → 1.7.1

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/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@
4
4
 
5
5
  Entries describe behavior at each release, not necessarily current behavior. Consult the READMEs and user guides for current usage.
6
6
 
7
+ ## 1.7.1 — 2026-11-07
8
+
9
+ ### 中文
10
+
11
+ - 插件更新优先接入官方 `updateBundle`,支持宿主热重载环境中的版本替换,由官方管理兼容性检查、安装和失败恢复。
12
+ - 更新结果区分已应用与需要重启,并明确提示旧版恢复结果。更新指引优先引导官方插件页的版本区域,保留旧 Host 兼容和本地开发版保护。
13
+
14
+ ### English
15
+
16
+ - Prefer the official `updateBundle` API for plugin updates, including Host hot-reload environments. The Host manages compatibility checks, replacement and recovery after failure.
17
+ - Distinguish applied updates from required restarts and report verified restoration outcomes. Direct manual guidance to the official Plugins page’s Versions section, retaining older Host compatibility and local-checkout protection.
18
+
7
19
  ## 1.7.0 — 2026-11-07
8
20
 
9
21
  ### 中文
package/README.md CHANGED
@@ -35,7 +35,7 @@ Archive Management adds conversation search, organization and backups to DeepSee
35
35
  - **Portable backups:** export and import JSON + Markdown ZIPs; imports preview before writing and skip existing IDs to avoid overwrites.
36
36
  - **Conversation lineage:** read-only Origins & Branches view traces sources, forks and subagent relationships across all workspaces, including archived and unarchived chats.
37
37
 
38
- > This document describes version 1.7.0. Previous recovery features are removed, with explicit retries for permanent-deletion tasks.
38
+ > This document describes version 1.7.1. Previous recovery features are removed, with explicit retries for permanent-deletion tasks.
39
39
 
40
40
  ## Install and open
41
41
 
package/README.zh-CN.md CHANGED
@@ -33,7 +33,7 @@
33
33
  - **可迁移备份:** 导出和导入 JSON + Markdown ZIP;导入前预览,跳过已有 ID,不覆盖现有数据。
34
34
  - **会话溯源:** 只读的「来源与分支」视图查看所有工作区中已归档、未归档会话的来源、分叉和子代理关系。
35
35
 
36
- > 本文描述 1.7.0,旧恢复功能已移除,永久删除支持任务重试。
36
+ > 本文描述 1.7.1,旧恢复功能已移除,永久删除支持任务重试。
37
37
 
38
38
  ## 安装与打开
39
39
 
@@ -129,7 +129,7 @@ Plugin confirmation, preview, import, and metadata dialogs share the opaque `--d
129
129
 
130
130
  Opening Archive Management loads local metadata, then automatically checks public npm metadata. While the page stays open, cache-aware checks run every 15 minutes and on visibility/network recovery. Success is cached for 12 hours and failure for 15 minutes; manual checks have a 30-second cooldown and concurrent requests coalesce. No chats or backups are sent. Failure never blocks chat management or claims the plugin is current. Leaving the page removes timers and listeners.
131
131
 
132
- `POST /about/install-update` additionally requires a local request, rejects cross-origin requests and accepts only `{ version }`. `createUpdateRuntime` checks profileContext, its dependency declaration and the actual loaded path. Local, external, unknown or mismatched sources cannot install. With host HMR present, it does not invoke installBundle, which rejects an already-installed package. Otherwise it delegates only to official pluginManager.installBundle without spawning a package manager or relaxing policy. Both the host outcome and installed package version must verify before reporting restart-required. Each Host has its own service instance; installation state and running version remain separate.
132
+ `POST /about/install-update` additionally requires a local request, rejects cross-origin requests and accepts only `{ version }`. `createUpdateRuntime` verifies the profile dependency and loaded package path. Local, external, unknown and mismatched sources cannot install. It prefers official `pluginManager.updateBundle`, including under HMR. The Host owns replacement and rollback. The adapter validates the nested result and on-disk package identity, reports verified restoration separately, and returns `applied` or `restart-required`. Older Hosts may use `installBundle` only without HMR. No package-manager process is spawned. Each Host owns its service state, and the loaded version remains distinct from the installed version. HMR may dispose this settings UI and abort its request, so users can reopen the page to verify the newly loaded version.
133
133
 
134
134
  ## Permanent deletion and crash recovery
135
135
 
@@ -132,7 +132,7 @@ apply 只接受凭据,不接受调用方增补会话 ID。每条执行前在
132
132
 
133
133
  打开归档管理时先读取本地信息,再自动查询公开 npm 版本;页面保持打开时每 15 分钟进行缓存感知检查,重新显示或联网时也会补查。成功结果缓存 12 小时,失败结果缓存 15 分钟;手动检查使用 30 秒短限流,并发请求合并。只查询公开版本,不发送聊天或备份内容;失败不阻塞会话管理,也不显示「已是最新版」。关闭页面会清理定时器和事件监听。
134
134
 
135
- `POST /about/install-update` 额外限制为本机请求,拒绝跨源,只接受 `{ version }`。`createUpdateRuntime` 读取当前 `profileContext`、profile 依赖声明及实际加载路径;本地、外部、未知或不一致的来源不会开放安装。宿主 `hmr` 存在时不调用会拒绝重复包的 `installBundle`。支持时只委托官方 `pluginManager.installBundle`,不自行运行包管理器或降低安装策略;安装结果及落盘包版本都验证后才报告待重启。服务实例按当前 Host 创建,安装状态与运行版本分别记录。
135
+ `POST /about/install-update` 额外要求本机请求,拒绝跨来源写入,仅接受 `{ version }`。`createUpdateRuntime` 核对 profile 依赖与实际加载路径,本地、外部、未知或路径不一致的来源均拒绝安装。优先调用官方 `pluginManager.updateBundle`,包括 HMR 环境,替换与回滚均由宿主负责。适配器校验嵌套结果和磁盘包身份,分别报告已应用、待重启及旧版恢复结果。旧 Host 仅在无 HMR 时退回 `installBundle`,不自行启动包管理器。各 Host 独立保存操作状态,已加载版本与已安装版本分开表达。HMR 可能卸载当前设置页并中断其请求,用户可重新打开页面核对新加载的版本。
136
136
 
137
137
  ## 永久删除与崩溃恢复
138
138
 
@@ -187,7 +187,7 @@ When diagnosing an unchanged version after an update, also verify that you updat
187
187
 
188
188
  Check for updates and View update beside the heading open a dialog with the running version, public release, last check and current host. Copy the package spec into Add plugin in another host. A terminal command is offered only when the current launcher and profile can be verified; official Desktop uses its own bundled command, never an unrelated dsh on PATH. This detects the current host only, not other Desktop installations.
189
189
 
190
- Local link:/file: installations are labeled Local checkout, offer Copy local path and are protected on the backend against registry replacement. Registry installations show Install update only when the host manager permits replacement; the official HMR restriction is retained, with manual guidance instead. Installation submits only the exact reviewed version through the official manager, preserving compatibility, locking and build-script checks. Failures never report completion and duplicate submissions are blocked. Success shows Restart needed; the running version changes only after the backend restarts. DSH is never restarted automatically.
190
+ Local link:/file: installations are labeled Local checkout, offer Copy local path and are protected on the backend against registry replacement. Registry installations offer Install update when supported. The official updateBundle API is preferred, letting the Host check version compatibility, replace the plugin and attempt to restore the previous version if installation fails. Settings may close during replacement. Reopen Archive Management afterward to verify its running version. Results distinguish Host-applied updates from required restarts and report whether restoration was verified. Older Hosts use the official installation API only where replacement is supported, otherwise directing users to the Versions section on the official Plugins page. Duplicate submissions are blocked. Restarting DSH remains a user action.
191
191
 
192
192
  ## Upgrades
193
193
 
@@ -187,7 +187,7 @@ JSON 是机器可读记录,Markdown 是可读副本。新版 Host 导出 ZIP v
187
187
 
188
188
  「检查更新」与标题旁的「查看更新」打开更新弹窗,展示运行版本、公开版本、上次检查和当前环境。支持复制包名到另一端的「添加插件」;命令只在能验证当前启动程序和 profile 时提供,官方桌面端使用其自带命令,不依赖 PATH 中的另一套 dsh。这里只识别当前环境,不扫描或自动安装到其他桌面端。
189
189
 
190
- 本地 `link:` / `file:` 等来源标记为「本地开发版」,提供复制本地路径,后端拒绝用线上包覆盖。普通 npm 安装仅在宿主插件管理器允许原地更新时显示「安装更新」;官方 HMR 模式要求重新安装时,保留宿主限制并给出手动指引。点击安装仅提交已展示的精确版本,宿主负责兼容性、安装锁和安装脚本检查;插件不绕过这些限制。失败不宣称完成,重复提交被阻止。安装成功后显示待重启,运行版本直到后端重启才改变;不会自动重启 DSH。
190
+ 本地 `link:` / `file:` 等来源标记为「本地开发版」,提供复制本地路径,后端拒绝用线上包覆盖。npm 安装在宿主支持时提供「安装更新」,优先调用官方 `updateBundle`,由宿主检查版本与兼容性、替换旧插件,并在安装失败时尝试恢复旧版本。更新期间设置页可能关闭,完成后重新打开归档管理核对运行版本。结果区分「宿主已应用」和「需要重启」;旧版恢复成功或未能确认恢复均有明确提示。旧 Host 缺少更新接口时,仅在允许替换的环境使用官方安装接口,其余引导前往官方插件页的版本区域更新。重复提交被阻止,DSH 重启由用户操作。
191
191
 
192
192
  ## 升级
193
193
 
package/lib/about.js CHANGED
@@ -70,8 +70,8 @@ export function createUpdateRuntime(ctx, {
70
70
  if (realpathSync(installedRoot) !== realpathSync(packageRoot)) { info.reason = 'runtime-mismatch'; return { info }; }
71
71
  info.command = commandFor(context, info.packageSpec);
72
72
  const manager = service('pluginManager');
73
- // Current official HMR explicitly rejects replacing an installed package. Preserve that boundary.
74
- info.canInstall = typeof manager?.installBundle === 'function' && service('hmr') == null;
73
+ info.canInstall = typeof manager?.updateBundle === 'function'
74
+ || (typeof manager?.installBundle === 'function' && service('hmr') == null);
75
75
  info.reason = info.canInstall ? null : 'host-managed';
76
76
  return { info, manager, installedRoot };
77
77
  } catch { return { info: { ...info, canInstall: false, reason: 'unknown-runtime', command: null } }; }
@@ -83,11 +83,31 @@ export function createUpdateRuntime(ctx, {
83
83
  const { info, manager, installedRoot } = inspect(version);
84
84
  if (info.source === 'local') throw updateError('update-local-install');
85
85
  if (!info.canInstall) throw updateError('update-host-managed');
86
- const result = await manager.installBundle(`${manifest.name}@${version}`, { registry: 'https://registry.npmjs.org/', saveExact: true });
86
+ const installedMatches = expected => {
87
+ try {
88
+ const installed = readJson(join(installedRoot, 'package.json'));
89
+ return installed.name === manifest.name && installed.version === expected;
90
+ } catch { return false; }
91
+ };
92
+ let result;
93
+ if (typeof manager.updateBundle === 'function') {
94
+ const update = await manager.updateBundle(manifest.name, version, { registry: 'https://registry.npmjs.org/' });
95
+ if (update?.to !== version) throw updateError('update-install-failed', 502);
96
+ if (update.restore) {
97
+ const restored = ['applied', 'restart-required'].includes(update.restore.application)
98
+ && !update.restore.error && update.restore.bundle === manifest.name
99
+ && update.restore.version === update.from && installedMatches(update.from);
100
+ throw updateError(restored ? 'update-restored' : 'update-restore-failed', 502);
101
+ }
102
+ if (update.step !== 'install') throw updateError('update-install-failed', 502);
103
+ result = update.result;
104
+ } else {
105
+ result = await manager.installBundle(`${manifest.name}@${version}`, { registry: 'https://registry.npmjs.org/', saveExact: true });
106
+ }
87
107
  if (!['applied', 'restart-required'].includes(result?.application) || result?.error
88
108
  || result?.version !== version || result?.bundle !== manifest.name) throw updateError('update-install-failed', 502);
89
- const installed = readJson(join(installedRoot, 'package.json'));
90
- if (installed.name !== manifest.name || installed.version !== version) throw updateError('update-install-unverified', 502);
109
+ if (!installedMatches(version)) throw updateError('update-install-unverified', 502);
110
+ return { application: result.application };
91
111
  },
92
112
  };
93
113
  }
@@ -344,6 +364,7 @@ export function createAboutService({
344
364
  },
345
365
  async installUpdate({ version: targetVersion } = {}) {
346
366
  if (install.status === 'installing') throw updateError('update-busy');
367
+ if (install.status === 'applied') throw updateError('update-already-applied');
347
368
  if (install.status === 'restart-required') throw updateError('update-restart-required');
348
369
  const environment = runtime?.describe(targetVersion);
349
370
  if (environment?.source === 'local') throw updateError('update-local-install');
@@ -352,8 +373,8 @@ export function createAboutService({
352
373
  try {
353
374
  await service.checkUpdates({ force: false });
354
375
  if (update.status !== 'available' || update.latestVersion !== targetVersion || !parseSemver(targetVersion)) throw updateError('update-version-changed');
355
- await runtime.install(targetVersion);
356
- install = { status: 'restart-required', version: targetVersion };
376
+ const result = await runtime.install(targetVersion);
377
+ install = { status: result?.application === 'applied' ? 'applied' : 'restart-required', version: targetVersion };
357
378
  return about();
358
379
  } catch (error) {
359
380
  install = { status: 'failed', version: null };
package/lib/client.js CHANGED
@@ -4568,13 +4568,15 @@ window.__ModuleLoader__.load({
4568
4568
  const [copied, setCopied] = _react.useState(null);
4569
4569
  _react.useEffect(() => { setCopied(null); }, [info?.command, info?.packageSpec, info?.localPath]);
4570
4570
  const local = info?.source === "local", restart = about?.install?.status === "restart-required";
4571
- const canInstall = info?.canInstall === true && update?.status === "available" && !local && !restart;
4571
+ const applied = about?.install?.status === "applied";
4572
+ const canInstall = info?.canInstall === true && update?.status === "available" && !local && !restart && !applied;
4572
4573
  const copy = async (value, key) => {
4573
4574
  try { if (!globalThis.navigator?.clipboard?.writeText) throw new Error("clipboard unavailable");
4574
4575
  await globalThis.navigator.clipboard.writeText(value); setCopied(key);
4575
4576
  } catch { setCopied("failed"); }
4576
4577
  };
4577
4578
  const status = restart ? (zh ? `v${about.install.version} 已安装,请重启 DSH 后确认运行版本。` : `v${about.install.version} installed. Restart DSH and verify the running version.`)
4579
+ : applied ? (zh ? `宿主已应用 v${about.install.version},重新打开归档管理可确认运行版本。` : `The host applied v${about.install.version}. Reopen Archive Management to verify the running version.`)
4578
4580
  : installing ? (zh ? "正在通过宿主安装更新…" : "Installing through the host…")
4579
4581
  : checking ? (zh ? "正在检查更新…" : "Checking for updates…")
4580
4582
  : update?.status === "available" ? (zh ? `发现新版本 v${update.latestVersion}` : `Version ${update.latestVersion} is available`)
@@ -4594,14 +4596,14 @@ window.__ModuleLoader__.load({
4594
4596
  (0, jsx.jsx)("dt", { children: zh ? "上次检查" : "Last checked" }), (0, jsx.jsx)("dd", { children: update?.checkedAt ? new Date(update.checkedAt).toLocaleString(zh ? "zh-CN" : "en-US") : "—" }),
4595
4597
  ] }),
4596
4598
  (0, jsx.jsxs)("div", { className: "dac-about-links", children: [
4597
- (0, jsx.jsx)(ControlButton, { type: "button", className: "dac-action-trigger", disabled: checking || installing || restart, onClick: onCheck, children: zh ? "重新检查" : "Check again" }),
4599
+ (0, jsx.jsx)(ControlButton, { type: "button", className: "dac-action-trigger", disabled: checking || installing || restart || applied, onClick: onCheck, children: zh ? "重新检查" : "Check again" }),
4598
4600
  safeAboutLink(about?.links?.changelog) && (0, jsx.jsx)("a", { href: safeAboutLink(about.links.changelog), target: "_blank", rel: "noopener noreferrer", children: zh ? "更新日志" : "Changelog" }),
4599
4601
  ] }),
4600
4602
  local ? (0, jsx.jsxs)("section", { className: "dac-update-manual", children: [
4601
4603
  (0, jsx.jsx)("p", { children: zh ? "当前连接本地源码,不会被线上更新替换。修改后重启 DSH 即可加载;在另一端接入时,粘贴下面目录到「添加插件」。" : "This local checkout is protected from registry replacement. Restart DSH after editing. To connect it in another host, paste this directory into Add plugin." }),
4602
4604
  codeBlock(info.localPath, zh ? "复制本地路径" : "Copy local path", "path"),
4603
4605
  ] }) : (0, jsx.jsxs)("section", { className: "dac-update-manual", children: [
4604
- (0, jsx.jsx)("p", { children: info?.source === "external" ? (zh ? "当前由外部来源安装,请按原来源更新。以下信息可用于在其他环境安装公开版本。" : "This plugin uses an external source; update from that source. Use the information below to install a public release elsewhere.") : canInstall ? (zh ? "点击「安装更新」仅更新当前环境的归档插件。安装完成后手动重启 DSH。" : "Install update changes only this plugin in the current host. Restart DSH manually afterward.") : (zh ? "当前环境请通过插件管理页面更新;宿主要求重新安装时,按页面提示操作。可复制下方包名到「添加插件」。" : "Update through the host’s plugin page; follow its reinstall instructions if required. Paste the package below into Add plugin." ) }),
4606
+ (0, jsx.jsx)("p", { children: info?.source === "external" ? (zh ? "当前由外部来源安装,请按原来源更新。以下信息可用于在其他环境安装公开版本。" : "This plugin uses an external source; update from that source. Use the information below to install a public release elsewhere.") : info?.canInstall ? (zh ? "点击「安装更新」由官方插件管理器检查兼容性并替换当前插件。更新期间本页可能关闭,完成后重新打开归档管理确认版本;需要重启时会明确提示。" : "Install update lets the official plugin manager check compatibility and replace this plugin. This page may close during replacement. Reopen Archive Management to verify the version. A required restart is reported explicitly.") : (zh ? "请前往官方插件页,找到归档管理,在版本区域更新;旧宿主要求重新安装时,按页面提示操作。下方包名也可用于在其他环境添加插件。" : "Find Archive Management on the official Plugins page and update from its Versions section. Follow reinstall instructions on older hosts. The package below can also be added in another host." ) }),
4605
4607
  codeBlock(info?.packageSpec ?? "dsh-archived-chats@latest", zh ? "复制安装信息" : "Copy package", "package"),
4606
4608
  info?.command && (0, jsx.jsxs)("details", { className: "dac-update-terminal", children: [
4607
4609
  (0, jsx.jsx)("summary", { children: zh ? "终端更新命令" : "Terminal update command" }),
@@ -4887,13 +4889,13 @@ window.__ModuleLoader__.load({
4887
4889
  };
4888
4890
  const installUpdate = async () => {
4889
4891
  if (updateRequestRef.current || about?.installation?.canInstall !== true || about?.installation?.source === "local"
4890
- || about?.update?.status !== "available" || about?.install?.status === "restart-required") return;
4892
+ || about?.update?.status !== "available" || ["applied", "restart-required"].includes(about?.install?.status)) return;
4891
4893
  const request = new AbortController(); updateRequestRef.current = request;
4892
4894
  aboutRequestRef.current?.controller.abort(); aboutRequestRef.current = null;
4893
4895
  setAboutChecking(false); setUpdateBusy(true); setUpdateError(null);
4894
4896
  try {
4895
4897
  const result = await post("/about/install-update", { version: about.update.latestVersion }, request.signal);
4896
- if (!validAbout(result) || result.install?.status !== "restart-required") throw new Error("update-unverified");
4898
+ if (!validAbout(result) || !["applied", "restart-required"].includes(result.install?.status)) throw new Error("update-unverified");
4897
4899
  if (!request.signal.aborted) setAbout(result);
4898
4900
  } catch (error) {
4899
4901
  if (!request.signal.aborted) {
@@ -4901,6 +4903,9 @@ window.__ModuleLoader__.load({
4901
4903
  setUpdateError(code === "update-version-changed" ? (isZh(t) ? "版本信息已变化,请重新检查后再更新。" : "Version information changed. Check again before updating.")
4902
4904
  : code === "update-local-install" ? (isZh(t) ? "当前已切换为本地开发版,已阻止线上覆盖。" : "This is now a local checkout; registry replacement was blocked.")
4903
4905
  : code === "update-host-managed" ? (isZh(t) ? "当前宿主要求通过插件管理页面更新,请按下方指引操作。" : "Use the host’s plugin manager to update. Follow the instructions below.")
4906
+ : code === "update-already-applied" ? (isZh(t) ? "宿主已应用更新,请重新打开归档管理确认运行版本。" : "The host already applied the update. Reopen Archive Management to verify the running version.")
4907
+ : code === "update-restored" ? (isZh(t) ? "新版安装失败,官方插件管理器已恢复旧版本。请重新打开归档管理确认。" : "The update failed and the official plugin manager restored the previous version. Reopen Archive Management to verify.")
4908
+ : code === "update-restore-failed" ? (isZh(t) ? "新版安装失败,旧版本也未能确认恢复。请到官方插件页检查并重新安装。" : "The update failed and restoration could not be verified. Check the official Plugins page and reinstall.")
4904
4909
  : (isZh(t) ? "未能确认更新完成。请重新打开本页核对状态;仍未完成时,通过宿主插件页面检查错误或使用下方命令。" : "Update completion could not be confirmed. Reopen this page to check its state, then use the host’s plugin page or the command below if needed."));
4905
4910
  }
4906
4911
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-archived-chats",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "查看、搜索和管理会话归档,支持工作区批量归档、标签备注、ZIP 备份和会话谱系。",
5
5
  "license": "MIT",
6
6
  "author": "Ultronen",