dsh-archived-chats 1.4.4 → 1.4.6
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 +26 -0
- package/README.md +16 -3
- package/README.zh-CN.md +16 -3
- package/assets/screenshots/preview-01.png +0 -0
- package/assets/screenshots/preview-02.png +0 -0
- package/assets/screenshots/preview-03.png +0 -0
- package/assets/screenshots/preview-04.png +0 -0
- package/assets/screenshots/preview-05.png +0 -0
- package/assets/screenshots/preview-06.png +0 -0
- package/assets/screenshots/preview-07.png +0 -0
- package/assets/screenshots/preview-08.png +0 -0
- package/docs/ARCHITECTURE.en.md +4 -4
- package/docs/ARCHITECTURE.md +4 -4
- package/docs/USER_GUIDE.md +2 -2
- package/docs/USER_GUIDE.zh-CN.md +2 -2
- package/lib/deletion-safety.js +50 -21
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,32 @@
|
|
|
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.4.6 — 2026-10-02
|
|
8
|
+
|
|
9
|
+
### 中文
|
|
10
|
+
|
|
11
|
+
- 修复会话库同时保留旧版 v3 与当前 v4 日志时,任意旧会话会使所有永久删除、清空回收站及自动回收清理报 `session-location-unavailable` 的问题(#55)。Host 的 `locate()` 返回当前代写入路径,不能据此要求该文件已经存在。
|
|
12
|
+
- 删除安全检查以真实会话独占目录为范围,兼容实际存在的规范代际日志及尚未生成日志的空目录;清单中的已不存在目录不再阻塞删除和中断任务重试。继续拒绝符号链接/junction、非文件日志、目录重叠、不属于会话的路径及不可读清单。
|
|
13
|
+
- 新增混合日志、空/缺失目录和删除安全回归,以及使用官方后端验证单条永久删除、归档直接删除、清空回收站的集成用例。原生 CI 门禁增加到六项;另在隔离目录验证官方 `0.2.0-rc.2` 后端,并在可选 peer 范围中显式加入这个已验证版本。
|
|
14
|
+
|
|
15
|
+
### English
|
|
16
|
+
|
|
17
|
+
- Fix `session-location-unavailable` blocking every permanent deletion, Empty Recycle Bin, and automatic recycle purge when the inventory contains a historical v3 log beside current v4 logs (#55). The Host's `locate()` names the current append target, which may not exist yet.
|
|
18
|
+
- Use the actual exclusive session directory as the deletion scope, accepting canonical stored generations and unmaterialized empty directories. Absent inventory directories no longer block deletion or interrupted-purge retries. Keep refusing links/junctions, non-file logs, overlapping directories, non-session-scoped paths, and unreadable inventory.
|
|
19
|
+
- Add mixed-generation, empty/missing-directory, and safety regressions plus an official-backend integration for individual purge, direct archive deletion, and Empty Recycle Bin. The mandatory native CI gate now runs six cases. Also verify the official `0.2.0-rc.2` backend in an isolated fixture and explicitly admit that tested version in the optional peer range.
|
|
20
|
+
|
|
21
|
+
## 1.4.5 — 2026-09-27
|
|
22
|
+
|
|
23
|
+
### 中文
|
|
24
|
+
|
|
25
|
+
- 使用隔离的虚构示例数据重拍八张插件界面截图,替换仍显示 1.3.1 旧界面及已移除快照入口的图片;中英文 README 与插件市场截图清单使用相同文件和顺序。
|
|
26
|
+
- 同步中英文 README、用户指南及架构文档的版本说明,突出当前 `0.1.7-rc.2` Host fixture 与 v4 会话格式验证。本版不改动运行时代码、数据格式或 Host 兼容范围。
|
|
27
|
+
|
|
28
|
+
### English
|
|
29
|
+
|
|
30
|
+
- Replace all eight 1.3.1 screenshots, which showed retired snapshot UI, with captures of the current plugin using isolated synthetic example data. Both READMEs and the marketplace screenshot list reference the same files in the same order.
|
|
31
|
+
- Update version notes in both READMEs, user guides, and architecture documents to describe the current `0.1.7-rc.2` Host fixture and v4 session-format verification. Runtime code, data format, and Host compatibility range are unchanged.
|
|
32
|
+
|
|
7
33
|
## 1.4.4 — 2026-09-27
|
|
8
34
|
|
|
9
35
|
### 中文
|
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Archive Management gives DeepSeek Harness a place to find chats hidden from the
|
|
|
32
32
|
|
|
33
33
|
> The English name is **Archive Management**, and the Chinese name is **归档管理**. The installation package remains `dsh-archived-chats`.
|
|
34
34
|
>
|
|
35
|
-
> This document follows the `main` branch
|
|
35
|
+
> This document follows the `main` branch and targets 1.4.6. This release fixes permanent deletion and Recycle Bin cleanup when historical and current session log generations coexist. See the [changelog](CHANGELOG.md) for release history.
|
|
36
36
|
|
|
37
37
|
## Quick start
|
|
38
38
|
|
|
@@ -66,6 +66,19 @@ dsh plugin --profile web update dsh-archived-chats
|
|
|
66
66
|
|
|
67
67
|
The five views are **Archived**, **Recycle Bin**, **Storage & Retention**, **Origins & Branches**, and **About**. Archiving does not create historical versions; Recycle Bin protection snapshots support recovery, not a browsable version-history library.
|
|
68
68
|
|
|
69
|
+
## Screenshots
|
|
70
|
+
|
|
71
|
+
These screenshots show the current interface with synthetic example chats; they contain no private conversations. The [marketplace screenshot list](screenshots.json) uses the same files in the same order.
|
|
72
|
+
|
|
73
|
+
1. [Archived chats](assets/screenshots/preview-01.png)
|
|
74
|
+
2. [Full-text search](assets/screenshots/preview-02.png)
|
|
75
|
+
3. [Workspace bulk archive](assets/screenshots/preview-03.png)
|
|
76
|
+
4. [Read-only preview](assets/screenshots/preview-04.png)
|
|
77
|
+
5. [Recycle Bin](assets/screenshots/preview-05.png)
|
|
78
|
+
6. [Restore confirmation](assets/screenshots/preview-06.png)
|
|
79
|
+
7. [Storage & Retention](assets/screenshots/preview-07.png)
|
|
80
|
+
8. [Origins & Branches](assets/screenshots/preview-08.png)
|
|
81
|
+
|
|
69
82
|
The Archived header has **Bulk archive** and **More**. More contains Import backup, Export all, Unarchive all, then Delete all after a separator. The Recycle Bin header directly shows **Restore all** and **Empty Recycle Bin** without a More menu. Recycle rows retain the preview icon and use compact **Restore** and **Delete** text buttons. Workspace and global restoration separately confirm scope, counts, and the Archived destination, skipping pending deletions. Empty Recycle Bin still requires irreversible-action confirmation. The header keeps consistent dimensions across tabs. Workspace actions and global export, unarchive, and deletion ask you to confirm the complete archive scope and chat count.
|
|
70
83
|
|
|
71
84
|
**About** shows the running version, author, license, guides, project and feedback links, and Check for updates. Opening the page checks public npm version metadata in the background, at most once every 12 hours during a running backend session; failures never block archive management. A newer version adds a **Get update** link beside the title, opening the plugin market without installing or restarting anything. Follow the host's restart guidance when convenient; a browser refresh alone may not load an updated backend. No chat or backup content is sent during update checks.
|
|
@@ -121,7 +134,7 @@ The package declares DSH `>=0.1.0-rc.7`; individual features depend on public Ho
|
|
|
121
134
|
|
|
122
135
|
Recycle Bin preview still depends on the original session; a missing original may prevent preview even when a protection snapshot can restore it. See [compatibility and limits](docs/USER_GUIDE.md#compatibility-and-limits).
|
|
123
136
|
|
|
124
|
-
The declared range remains capability-based. Release automation
|
|
137
|
+
The declared range remains capability-based. Release automation tests Node.js 18 on Ubuntu and Node.js 24 on Ubuntu, macOS, and Windows; the Node.js 24 matrix requires the official `@deepseek-ai/dsh-session@0.1.7-rc.2` Host backend integration (6/6) and package checks, including the v4 session format.
|
|
125
138
|
|
|
126
139
|
## Documentation
|
|
127
140
|
|
|
@@ -146,7 +159,7 @@ node scripts/run-native-integration.mjs
|
|
|
146
159
|
npm pack --dry-run --json
|
|
147
160
|
```
|
|
148
161
|
|
|
149
|
-
The suite covers Host and browser behavior, export/import, snapshot fallback recovery, Recycle Bin, retention, search, responsive layout, public types, package contents, and repository hygiene. The native commands install the locked `@deepseek-ai/dsh-session@0.1.7-rc.2` fixture and require all
|
|
162
|
+
The suite covers Host and browser behavior, export/import, snapshot fallback recovery, Recycle Bin, retention, search, responsive layout, public types, package contents, and repository hygiene. The native commands install the locked `@deepseek-ai/dsh-session@0.1.7-rc.2` fixture and require all six native integration cases to run without skips. All checks use isolated temporary data and never read real sessions.
|
|
150
163
|
|
|
151
164
|
## Uninstall
|
|
152
165
|
|
package/README.zh-CN.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
> 中文名称为「归档管理」,英文名称为 Archive Management;安装包名始终为 `dsh-archived-chats`。
|
|
32
32
|
>
|
|
33
|
-
> 本文跟随 `main`
|
|
33
|
+
> 本文跟随 `main` 分支维护,适用于 1.4.6。本版修复旧版与当前版本会话日志混存时,永久删除及回收站清空失败的问题。版本变更见[更新日志](CHANGELOG.md)。
|
|
34
34
|
|
|
35
35
|
## 快速开始
|
|
36
36
|
|
|
@@ -64,6 +64,19 @@ dsh plugin --profile web update dsh-archived-chats
|
|
|
64
64
|
|
|
65
65
|
五个视图为 **已归档**、**回收站**、**空间与策略**、**来源与分支** 和 **关于**。归档不会创建历史版本;回收站保护快照仅用于恢复,不是可浏览的历史版本库。
|
|
66
66
|
|
|
67
|
+
## 界面截图
|
|
68
|
+
|
|
69
|
+
以下截图使用虚构示例聊天展示当前界面,不含私人会话。[插件市场截图清单](screenshots.json)使用相同文件及顺序。
|
|
70
|
+
|
|
71
|
+
1. [已归档聊天](assets/screenshots/preview-01.png)
|
|
72
|
+
2. [全文搜索](assets/screenshots/preview-02.png)
|
|
73
|
+
3. [工作区批量归档](assets/screenshots/preview-03.png)
|
|
74
|
+
4. [只读预览](assets/screenshots/preview-04.png)
|
|
75
|
+
5. [回收站](assets/screenshots/preview-05.png)
|
|
76
|
+
6. [恢复确认](assets/screenshots/preview-06.png)
|
|
77
|
+
7. [空间与策略](assets/screenshots/preview-07.png)
|
|
78
|
+
8. [来源与分支](assets/screenshots/preview-08.png)
|
|
79
|
+
|
|
67
80
|
已归档页顶部仅保留 **批量归档** 和 **更多**;「更多」依次提供导入备份、全部导出、全部取消归档,分隔线后为全部删除。回收站顶部直接并列显示 **全部恢复** 和 **清空回收站**,不再设更多菜单;聊天行保留预览图标,使用紧凑的 **恢复** 和 **删除** 文字按钮。工作区恢复和全局恢复分别确认范围、数量及已归档去向,跳过正在永久删除的条目;清空仍须确认不可恢复的后果。各 Tab 顶部保持一致尺寸。工作区操作及全局导出、取消归档、删除均先确认完整归档范围和聊天数量。
|
|
68
81
|
|
|
69
82
|
「关于」展示当前运行版本、作者、许可证、指南、项目与反馈链接,提供「检查更新」。打开页面时后台查询公开 npm 版本信息,运行期间自动检查间隔至少 12 小时;失败不会阻断归档功能。有新版时标题旁显示「去更新」,打开插件市场,不自动安装或重启。更新后按宿主提示自行安排重启 DSH,仅刷新网页不一定能加载新版后端。
|
|
@@ -119,7 +132,7 @@ dsh plugin --profile web update dsh-archived-chats
|
|
|
119
132
|
|
|
120
133
|
回收站预览目前仍依赖原会话;原件丢失时,即使保护快照可恢复,也可能无法预览。详见[兼容性与限制](docs/USER_GUIDE.zh-CN.md#兼容性与限制)。
|
|
121
134
|
|
|
122
|
-
声明的版本范围仍以 Host
|
|
135
|
+
声明的版本范围仍以 Host 公开能力为准。发布自动化在 Ubuntu 上测试 Node.js 18,并在 Ubuntu、macOS 和 Windows 上测试 Node.js 24;Node.js 24 矩阵强制运行基于官方 `@deepseek-ai/dsh-session@0.1.7-rc.2` 的 Host 后端集成(6/6)与打包检查,覆盖 v4 会话格式。
|
|
123
136
|
|
|
124
137
|
## 文档
|
|
125
138
|
|
|
@@ -144,7 +157,7 @@ node scripts/run-native-integration.mjs
|
|
|
144
157
|
npm pack --dry-run --json
|
|
145
158
|
```
|
|
146
159
|
|
|
147
|
-
测试覆盖 Host 与浏览器行为、导出导入、快照恢复、回收站、保留策略、全文搜索、响应式布局、公开类型、包内容和仓库卫生。原生集成命令会安装锁定的 `@deepseek-ai/dsh-session@0.1.7-rc.2` fixture
|
|
160
|
+
测试覆盖 Host 与浏览器行为、导出导入、快照恢复、回收站、保留策略、全文搜索、响应式布局、公开类型、包内容和仓库卫生。原生集成命令会安装锁定的 `@deepseek-ai/dsh-session@0.1.7-rc.2` fixture,并要求六个原生集成用例全部执行且不能跳过。所有检查只使用隔离临时数据,不读取真实会话。
|
|
148
161
|
|
|
149
162
|
## 卸载
|
|
150
163
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/ARCHITECTURE.en.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English · [中文](ARCHITECTURE.md) · [User guide](USER_GUIDE.md)
|
|
4
4
|
|
|
5
|
-
This document follows `main
|
|
5
|
+
This document follows `main` and covers 1.4.6 behavior. This release fixes permanent deletion and Recycle Bin cleanup when historical and current session log generations coexist. See the [changelog](../CHANGELOG.md) for release history. Runtime code is the authority for interfaces and behavior; user-facing documentation should agree with it.
|
|
6
6
|
|
|
7
7
|
## Product boundary and modules
|
|
8
8
|
|
|
@@ -159,7 +159,7 @@ Move is missing → trashed; recycle purge is trashed/degraded → purge-pending
|
|
|
159
159
|
|
|
160
160
|
Both paths share purge: persist intent → remove and recheck all associated snapshots → physically delete the session → finish registry/metadata cleanup → remove the recycle record last. Physical deletion runs with the caller's lifecycle lock and a pending marker.
|
|
161
161
|
|
|
162
|
-
Snapshot sweeping uses manifest ownership and the record's named snapshotId to cover corrupted protection data. Unrelated unassignable corruption does not block a session's purge. The log must reside in a directory named for that session ID; shared directories are not purge targets. A missing log or archive index is not proof of completion: durable intent authorizes remaining cleanup.
|
|
162
|
+
Snapshot sweeping uses manifest ownership and the record's named snapshotId to cover corrupted protection data. Unrelated unassignable corruption does not block a session's purge. The log must reside in a directory named for that session ID; shared directories are not purge targets. Safety checks use the actual session directory as the removal scope. If the current-generation log named by `locate()` is absent, inspect canonical generation logs that exist in the directory; empty directories still undergo ancestry and overlap checks. Inventory entries with absent directories are treated as missing and do not block other purges. Links/junctions, non-file logs, overlapping directories, and unreadable inventory still refuse deletion. A missing log or archive index is not proof of completion: durable intent authorizes remaining cleanup.
|
|
163
163
|
|
|
164
164
|
Failures retain purge-pending. Startup and runtime retries continue these tasks, never restore them to ordinary chats. Archive deletion rejects existing recycle records, protecting the Recycle Bin from archive-wide Delete all. Purging snapshot copies does not promise global attachment cleanup or Host session_projcache eviction; no corresponding safe public eviction API is used.
|
|
165
165
|
|
|
@@ -214,6 +214,6 @@ npm pack --dry-run --json
|
|
|
214
214
|
git diff --check
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
-
The native commands install the locked `@deepseek-ai/dsh-session@0.1.7-rc.2` fixture and require all
|
|
217
|
+
The native commands install the locked `@deepseek-ai/dsh-session@0.1.7-rc.2` fixture and require all six native integration cases to run without skips. This is the local equivalent of the mandatory native Host integration gate in CI.
|
|
218
218
|
|
|
219
|
-
The declared DSH `>=0.1.0-rc.7` range remains capability-based. Release automation
|
|
219
|
+
The declared DSH `>=0.1.0-rc.7` range remains capability-based. Release automation tests Node.js 18 on Ubuntu and Node.js 24 on Ubuntu, macOS, and Windows; the Node.js 24 matrix requires the official `@deepseek-ai/dsh-session@0.1.7-rc.2` Host backend integration (6/6) and package checks, including the v4 session format. The [screenshots](../screenshots.json) show the current interface with synthetic example chats.
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](ARCHITECTURE.en.md) · 中文 · [用户指南](USER_GUIDE.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
本文跟随 `main` 分支,功能对应 1.4.
|
|
5
|
+
本文跟随 `main` 分支,功能对应 1.4.6。本版修复旧版与当前版本会话日志混存时,永久删除及回收站清空失败的问题。版本变更见[更新日志](../CHANGELOG.md)。运行时代码是接口与行为的依据;用户操作说明与本文应保持一致。
|
|
6
6
|
|
|
7
7
|
## 产品边界与模块
|
|
8
8
|
|
|
@@ -159,7 +159,7 @@ apply 只接受凭据,不接受调用方增补会话 ID。每条执行前在
|
|
|
159
159
|
|
|
160
160
|
两条路径共用 purge:持久化删除意图 → 清理该会话的全部关联快照并复查 → 物理删除会话 → 完成注册表与元数据清理 → 最后移除回收记录。物理删除在调用方持有生命周期锁且有待删除标记时执行。
|
|
161
161
|
|
|
162
|
-
快照清扫使用 manifest 身份归属,并点名记录引用的 snapshotId,以覆盖损坏快照;不相关且无法归属的损坏项不会阻塞该会话删除。会话文件必须位于以自身 ID
|
|
162
|
+
快照清扫使用 manifest 身份归属,并点名记录引用的 snapshotId,以覆盖损坏快照;不相关且无法归属的损坏项不会阻塞该会话删除。会话文件必须位于以自身 ID 命名的独占目录中,共享目录不是合法删除目标。安全检查以真实会话目录为删除范围:`locate()` 返回的当前代日志不存在时,检查目录内实际存在的规范代际日志;空目录仍须通过祖先与目录重叠检查。目录已不存在的清单项视为缺失,不阻断其余删除。符号链接/junction、非文件日志、目录重叠及无法读取的清单仍拒绝删除。已删除日志或索引不代表任务完成:依靠持久化意图继续清理剩余状态。
|
|
163
163
|
|
|
164
164
|
失败保留 `purge-pending`。启动恢复和运行中的重试继续处理这些任务,不将其恢复为普通聊天。归档删除拒绝已有回收记录,所以归档页全部删除不会波及回收站。删除快照副本不承诺清除 Host 全局附件或 `session_projcache`;插件没有对应的安全公开 eviction API。
|
|
165
165
|
|
|
@@ -214,6 +214,6 @@ npm pack --dry-run --json
|
|
|
214
214
|
git diff --check
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
-
原生集成命令会安装锁定的 `@deepseek-ai/dsh-session@0.1.7-rc.2` fixture
|
|
217
|
+
原生集成命令会安装锁定的 `@deepseek-ai/dsh-session@0.1.7-rc.2` fixture,并要求六个原生集成用例全部执行且不能跳过;这是 CI 强制原生 Host 集成门禁的本地等价检查。
|
|
218
218
|
|
|
219
|
-
声明的 DSH `>=0.1.0-rc.7` 范围仍以 Host
|
|
219
|
+
声明的 DSH `>=0.1.0-rc.7` 范围仍以 Host 公开能力为准。发布自动化在 Ubuntu 上测试 Node.js 18,并在 Ubuntu、macOS 和 Windows 上测试 Node.js 24;Node.js 24 矩阵强制运行基于官方 `@deepseek-ai/dsh-session@0.1.7-rc.2` 的 Host 后端集成(6/6)与打包检查,覆盖 v4 会话格式。[截图](../screenshots.json)使用虚构示例聊天展示当前界面。
|
package/docs/USER_GUIDE.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English · [简体中文](USER_GUIDE.zh-CN.md) · [Back to README](../README.md)
|
|
4
4
|
|
|
5
|
-
Archive Management provides a place to browse and manage DSH's archived chats, plus workspace bulk archiving. This document follows the `main` branch
|
|
5
|
+
Archive Management provides a place to browse and manage DSH's archived chats, plus workspace bulk archiving. This document follows the `main` branch and targets 1.4.6. This release fixes permanent deletion and Recycle Bin cleanup when historical and current session log generations coexist. See the [changelog](../CHANGELOG.md) for release history. For interfaces and data formats, see the [architecture](ARCHITECTURE.en.md).
|
|
6
6
|
|
|
7
7
|
## Understand the three locations
|
|
8
8
|
|
|
@@ -198,7 +198,7 @@ Features depend on public Host capabilities, not just a version number:
|
|
|
198
198
|
- Version 2 protection records require this or a newer plugin. Before downgrading, restore recycled chats you need to retain and back up plugin data.
|
|
199
199
|
- If `trash.json` cannot be read, Archived is marked unverified, the Recycle Bin is unavailable, and archive mutations such as unarchive, tag/note editing, and deletion are refused instead of guessing.
|
|
200
200
|
|
|
201
|
-
The declared DSH `>=0.1.0-rc.7` range remains capability-based. Release automation
|
|
201
|
+
The declared DSH `>=0.1.0-rc.7` range remains capability-based. Release automation tests Node.js 18 on Ubuntu and Node.js 24 on Ubuntu, macOS, and Windows; the Node.js 24 matrix requires the official `@deepseek-ai/dsh-session@0.1.7-rc.2` Host backend integration (6/6) and package checks, including the v4 session format.
|
|
202
202
|
|
|
203
203
|
## Local data and uninstall
|
|
204
204
|
|
package/docs/USER_GUIDE.zh-CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](USER_GUIDE.md) · 简体中文 · [返回 README](../README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
归档管理为 DSH 已归档聊天提供查看和管理页面,并扩展按工作区批量归档。本文跟随 `main`
|
|
5
|
+
归档管理为 DSH 已归档聊天提供查看和管理页面,并扩展按工作区批量归档。本文跟随 `main` 分支维护,适用于 1.4.6。本版修复旧版与当前版本会话日志混存时,永久删除及回收站清空失败的问题。版本变更见[更新日志](../CHANGELOG.md),接口和数据格式见[架构说明](ARCHITECTURE.md)。
|
|
6
6
|
|
|
7
7
|
## 先理解三个位置
|
|
8
8
|
|
|
@@ -198,7 +198,7 @@ DSH 运行时约每分钟检查,关闭期间暂停,启动恢复完成后补
|
|
|
198
198
|
- v2 保护快照需要此版或更新的插件读取;回退旧插件前先恢复需要保留的回收聊天,并备份插件数据。
|
|
199
199
|
- 如果 `trash.json` 无法读取,归档列表会标为未经核对,回收站不可用,并拒绝取消归档、编辑标签备注及删除等归档修改,避免猜测状态。
|
|
200
200
|
|
|
201
|
-
声明的 DSH `>=0.1.0-rc.7` 范围仍表达基于 Host
|
|
201
|
+
声明的 DSH `>=0.1.0-rc.7` 范围仍表达基于 Host 公开能力的兼容性。发布自动化在 Ubuntu 上测试 Node.js 18,并在 Ubuntu、macOS 和 Windows 上测试 Node.js 24;Node.js 24 矩阵强制运行基于官方 `@deepseek-ai/dsh-session@0.1.7-rc.2` 的 Host 后端集成(6/6)与打包检查,覆盖 v4 会话格式。
|
|
202
202
|
|
|
203
203
|
## 本地数据与卸载
|
|
204
204
|
|
package/lib/deletion-safety.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { lstat, realpath } from 'node:fs/promises';
|
|
2
|
-
import { basename, dirname, isAbsolute, normalize, parse, relative, sep } from 'node:path';
|
|
1
|
+
import { lstat, readdir, realpath } from 'node:fs/promises';
|
|
2
|
+
import { basename, dirname, isAbsolute, join, normalize, parse, relative, sep } from 'node:path';
|
|
3
3
|
|
|
4
4
|
function failure(code, message, status) {
|
|
5
5
|
return Object.assign(new Error(message), { code, status });
|
|
@@ -24,13 +24,14 @@ function inside(parent, candidate) {
|
|
|
24
24
|
return offset === '' || (offset !== '..' && !offset.startsWith(`..${sep}`) && !isAbsolute(offset));
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
async function checkedStat(path, kind) {
|
|
27
|
+
async function checkedStat(path, kind, allowMissing = false) {
|
|
28
28
|
try {
|
|
29
29
|
const stat = await lstat(path);
|
|
30
30
|
if (stat.isSymbolicLink()) throw unsafe(`${kind} uses a symbolic link or junction`);
|
|
31
31
|
return stat;
|
|
32
32
|
} catch (cause) {
|
|
33
33
|
if (cause?.code === 'session-location-unsafe') throw cause;
|
|
34
|
+
if (allowMissing && cause?.code === 'ENOENT') return null;
|
|
34
35
|
throw unavailable(`${kind} cannot be inspected`);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
@@ -47,15 +48,47 @@ async function checkCanonicalDirectoryAncestry(path, checked) {
|
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
// Match the Host's committed generation names, with either physical encoding.
|
|
52
|
+
// Temporary files, v0, leading-zero versions and unsafe integers are not logs.
|
|
53
|
+
function isCommittedLog(filename) {
|
|
54
|
+
const match = /^session(?:\.v([1-9][0-9]*))?\.jsonl(?:\.zstd)?$/u.exec(filename);
|
|
55
|
+
return match !== null && (match[1] === undefined || Number.isSafeInteger(Number(match[1])));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function checkLogLocation(path, directory, allowMissing = false) {
|
|
59
|
+
const stat = await checkedStat(path, 'session location', allowMissing);
|
|
60
|
+
if (stat === null) return false;
|
|
61
|
+
if (!stat.isFile()) throw unsafe('session location is not a regular file');
|
|
62
|
+
let canonicalPath;
|
|
63
|
+
try { canonicalPath = await realpath(path); }
|
|
64
|
+
catch { throw unavailable('session location cannot be canonicalized'); }
|
|
65
|
+
if (!inside(directory, canonicalPath)) throw unsafe('session location escapes its directory');
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function checkSessionLogs(entry) {
|
|
70
|
+
if (await checkLogLocation(entry.path, entry.realDirectory, true)) return;
|
|
71
|
+
// locate() names the current append target even for historical-only or
|
|
72
|
+
// unmaterialized sessions. Inspect committed logs that actually exist;
|
|
73
|
+
// an empty directory is still the complete recursive-removal scope.
|
|
74
|
+
let filenames;
|
|
75
|
+
try { filenames = await readdir(entry.realDirectory); }
|
|
76
|
+
catch { throw unavailable('session directory cannot be inspected'); }
|
|
77
|
+
for (const filename of filenames) {
|
|
78
|
+
if (isCommittedLog(filename)) await checkLogLocation(join(entry.realDirectory, filename), entry.realDirectory);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
50
82
|
/**
|
|
51
83
|
* Resolve the recursively removable directory for one session. The Host's full
|
|
52
|
-
* current inventory is treated as authority: every
|
|
53
|
-
*
|
|
84
|
+
* current inventory is treated as authority: every existing directory must be
|
|
85
|
+
* session-scoped, non-linking, and disjoint from the requested tree. Existing
|
|
86
|
+
* logs provide additional evidence, but the current generation may not exist.
|
|
54
87
|
* The returned deletion directory is canonical and its complete ancestry has
|
|
55
88
|
* been checked, so a lexical platform alias or higher link is never passed to
|
|
56
89
|
* recursive removal.
|
|
57
|
-
*
|
|
58
|
-
* infer a path from stale registry data.
|
|
90
|
+
* Missing headers or directories are successful restart states, not permission
|
|
91
|
+
* to infer a path from stale registry data.
|
|
59
92
|
*/
|
|
60
93
|
export async function resolveExclusiveSessionDirectory(persistence, sessionId) {
|
|
61
94
|
if (!safeId(sessionId)) throw unsafe('session id is not a safe directory name');
|
|
@@ -95,30 +128,26 @@ export async function resolveExclusiveSessionDirectory(persistence, sessionId) {
|
|
|
95
128
|
const parent = dirname(entry.directory);
|
|
96
129
|
const storageRoot = dirname(parent);
|
|
97
130
|
if (storageRoot !== parse(storageRoot).root) {
|
|
98
|
-
const storageRootStat = await checkedStat(storageRoot, 'storage-root ancestor');
|
|
99
|
-
if (!storageRootStat.isDirectory()) throw unsafe('storage-root ancestor is not a directory');
|
|
131
|
+
const storageRootStat = await checkedStat(storageRoot, 'storage-root ancestor', true);
|
|
132
|
+
if (storageRootStat !== null && !storageRootStat.isDirectory()) throw unsafe('storage-root ancestor is not a directory');
|
|
100
133
|
}
|
|
101
|
-
const parentStat = await checkedStat(parent, 'session-root ancestor');
|
|
102
|
-
if (!parentStat.isDirectory()) throw unsafe('session-root ancestor is not a directory');
|
|
103
|
-
const directoryStat = await checkedStat(entry.directory, 'session directory');
|
|
134
|
+
const parentStat = await checkedStat(parent, 'session-root ancestor', true);
|
|
135
|
+
if (parentStat !== null && !parentStat.isDirectory()) throw unsafe('session-root ancestor is not a directory');
|
|
136
|
+
const directoryStat = await checkedStat(entry.directory, 'session directory', true);
|
|
137
|
+
if (directoryStat === null) continue;
|
|
104
138
|
if (!directoryStat.isDirectory()) throw unsafe('session directory is not a directory');
|
|
105
|
-
const fileStat = await checkedStat(entry.path, 'session location');
|
|
106
|
-
if (!fileStat.isFile()) throw unsafe('session location is not a regular file');
|
|
107
139
|
try {
|
|
108
140
|
entry.realDirectory = await realpath(entry.directory);
|
|
109
|
-
entry.realPath = await realpath(entry.path);
|
|
110
141
|
} catch { throw unavailable('session location cannot be canonicalized'); }
|
|
111
142
|
await checkCanonicalDirectoryAncestry(entry.realDirectory, checkedCanonicalDirectories);
|
|
112
|
-
|
|
143
|
+
await checkSessionLogs(entry);
|
|
113
144
|
}
|
|
114
145
|
|
|
115
|
-
const target = entries.find((entry) => entry.id === sessionId);
|
|
146
|
+
const target = entries.find((entry) => entry.id === sessionId && entry.realDirectory !== undefined);
|
|
116
147
|
if (target === undefined) return { status: 'missing', sessionDirectory: null };
|
|
117
148
|
for (const entry of entries) {
|
|
118
|
-
if (entry === target) continue;
|
|
119
|
-
if (inside(target.realDirectory, entry.
|
|
120
|
-
|| inside(entry.realDirectory, target.realPath)
|
|
121
|
-
|| inside(target.realDirectory, entry.realDirectory)
|
|
149
|
+
if (entry === target || entry.realDirectory === undefined) continue;
|
|
150
|
+
if (inside(target.realDirectory, entry.realDirectory)
|
|
122
151
|
|| inside(entry.realDirectory, target.realDirectory)) {
|
|
123
152
|
throw unsafe('session locations overlap');
|
|
124
153
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-archived-chats",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.6",
|
|
4
4
|
"description": "查看、搜索和恢复已归档会话,支持按工作区批量归档、备份导入导出与回收站管理。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ultronen",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
},
|
|
97
97
|
"peerDependencies": {
|
|
98
98
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
99
|
-
"@deepseek-ai/dsh-session": ">=0.1.0-rc.7 <0.1.1-0 || >=0.1.1-rc.1 <0.1.2-0 || >=0.1.2-alpha.1 <0.2.0-0 || 0.1.5-rc.2 || 0.1.7-rc.2",
|
|
99
|
+
"@deepseek-ai/dsh-session": ">=0.1.0-rc.7 <0.1.1-0 || >=0.1.1-rc.1 <0.1.2-0 || >=0.1.2-alpha.1 <0.2.0-0 || 0.1.5-rc.2 || 0.1.7-rc.2 || 0.2.0-rc.2",
|
|
100
100
|
"react": "^18.2.0"
|
|
101
101
|
},
|
|
102
102
|
"peerDependenciesMeta": {
|