dsh-archived-chats 1.4.5 → 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 +14 -0
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- 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,20 @@
|
|
|
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
|
+
|
|
7
21
|
## 1.4.5 — 2026-09-27
|
|
8
22
|
|
|
9
23
|
### 中文
|
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 and targets 1.4.
|
|
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
|
|
|
@@ -134,7 +134,7 @@ The package declares DSH `>=0.1.0-rc.7`; individual features depend on public Ho
|
|
|
134
134
|
|
|
135
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).
|
|
136
136
|
|
|
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 (
|
|
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.
|
|
138
138
|
|
|
139
139
|
## Documentation
|
|
140
140
|
|
|
@@ -159,7 +159,7 @@ node scripts/run-native-integration.mjs
|
|
|
159
159
|
npm pack --dry-run --json
|
|
160
160
|
```
|
|
161
161
|
|
|
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
|
|
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.
|
|
163
163
|
|
|
164
164
|
## Uninstall
|
|
165
165
|
|
package/README.zh-CN.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
> 中文名称为「归档管理」,英文名称为 Archive Management;安装包名始终为 `dsh-archived-chats`。
|
|
32
32
|
>
|
|
33
|
-
> 本文跟随 `main` 分支维护,适用于 1.4.
|
|
33
|
+
> 本文跟随 `main` 分支维护,适用于 1.4.6。本版修复旧版与当前版本会话日志混存时,永久删除及回收站清空失败的问题。版本变更见[更新日志](CHANGELOG.md)。
|
|
34
34
|
|
|
35
35
|
## 快速开始
|
|
36
36
|
|
|
@@ -132,7 +132,7 @@ dsh plugin --profile web update dsh-archived-chats
|
|
|
132
132
|
|
|
133
133
|
回收站预览目前仍依赖原会话;原件丢失时,即使保护快照可恢复,也可能无法预览。详见[兼容性与限制](docs/USER_GUIDE.zh-CN.md#兼容性与限制)。
|
|
134
134
|
|
|
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 后端集成(
|
|
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 会话格式。
|
|
136
136
|
|
|
137
137
|
## 文档
|
|
138
138
|
|
|
@@ -157,7 +157,7 @@ node scripts/run-native-integration.mjs
|
|
|
157
157
|
npm pack --dry-run --json
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
测试覆盖 Host 与浏览器行为、导出导入、快照恢复、回收站、保留策略、全文搜索、响应式布局、公开类型、包内容和仓库卫生。原生集成命令会安装锁定的 `@deepseek-ai/dsh-session@0.1.7-rc.2` fixture
|
|
160
|
+
测试覆盖 Host 与浏览器行为、导出导入、快照恢复、回收站、保留策略、全文搜索、响应式布局、公开类型、包内容和仓库卫生。原生集成命令会安装锁定的 `@deepseek-ai/dsh-session@0.1.7-rc.2` fixture,并要求六个原生集成用例全部执行且不能跳过。所有检查只使用隔离临时数据,不读取真实会话。
|
|
161
161
|
|
|
162
162
|
## 卸载
|
|
163
163
|
|
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` and covers 1.4.
|
|
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 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 (
|
|
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 公开能力为准。发布自动化在 Ubuntu 上测试 Node.js 18,并在 Ubuntu、macOS 和 Windows 上测试 Node.js 24;Node.js 24 矩阵强制运行基于官方 `@deepseek-ai/dsh-session@0.1.7-rc.2` 的 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 and targets 1.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 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 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 (
|
|
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` 分支维护,适用于 1.4.
|
|
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 公开能力的兼容性。发布自动化在 Ubuntu 上测试 Node.js 18,并在 Ubuntu、macOS 和 Windows 上测试 Node.js 24;Node.js 24 矩阵强制运行基于官方 `@deepseek-ai/dsh-session@0.1.7-rc.2` 的 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": {
|