@xrn1997/dsh-session-delete 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -201
- package/README.md +141 -129
- package/cordis.patch.yml +3 -3
- package/lib/client.js +2811 -1264
- package/lib/index.js +2368 -1277
- package/lib/types/api/attachment-orphans.d.ts +11 -0
- package/lib/types/api/attachment.d.ts +23 -0
- package/lib/types/api/dispatch.d.ts +13 -29
- package/lib/types/api/request-trust.d.ts +45 -0
- package/lib/types/api/route-table.d.ts +52 -18
- package/lib/types/api/trash-rows.d.ts +3 -3
- package/lib/types/client/attachment-view.d.ts +27 -0
- package/lib/types/client/batch-bar.d.ts +1 -1
- package/lib/types/client/batch-confirm.d.ts +2 -2
- package/lib/types/client/batch-menu-item.d.ts +1 -1
- package/lib/types/client/batch-select.d.ts +1 -1
- package/lib/types/client/busy-gate.d.ts +6 -2
- package/lib/types/client/confirm-close.d.ts +14 -0
- package/lib/types/client/context.d.ts +3 -2
- package/lib/types/client/copy.d.ts +102 -42
- package/lib/types/client/delete-confirm.d.ts +1 -3
- package/lib/types/client/delete-menu-item.d.ts +1 -1
- package/lib/types/client/host-rows.d.ts +5 -1
- package/lib/types/client/main-view.d.ts +1 -1
- package/lib/types/client/maintenance-parts.d.ts +25 -0
- package/lib/types/client/maintenance-settings.d.ts +22 -0
- package/lib/types/client/markdown.d.ts +9 -2
- package/lib/types/client/orphan-maintenance.d.ts +16 -13
- package/lib/types/client/receipt.d.ts +44 -2
- package/lib/types/client/remote.d.ts +38 -50
- package/lib/types/client/store.d.ts +14 -3
- package/lib/types/client/trash-confirm.d.ts +2 -15
- package/lib/types/client/trash-detail.d.ts +48 -6
- package/lib/types/client/trash-entry.d.ts +1 -1
- package/lib/types/client/trash-nav.d.ts +12 -8
- package/lib/types/client/trash-panel.d.ts +4 -3
- package/lib/types/client/ui/Checkbox.d.ts +1 -1
- package/lib/types/client/ui/Modal.d.ts +2 -2
- package/lib/types/client/ui/Toast.d.ts +3 -1
- package/lib/types/client/ui/icons.d.ts +7 -12
- package/lib/types/client/ui/top-layer.d.ts +10 -0
- package/lib/types/client/undo-notices.d.ts +52 -0
- package/lib/types/client/undo-toast.d.ts +6 -59
- package/lib/types/client/use-column-resize.d.ts +9 -20
- package/lib/types/client/use-maintenance-section.d.ts +52 -0
- package/lib/types/client/use-maintenance.d.ts +17 -0
- package/lib/types/client/use-panel-width.d.ts +1 -1
- package/lib/types/client/use-trash-panel-controller.d.ts +54 -16
- package/lib/types/client/use-trash-query.d.ts +39 -34
- package/lib/types/client/use-trash-selection.d.ts +15 -12
- package/lib/types/client/write-seats.d.ts +36 -28
- package/lib/types/client/write-settlement.d.ts +11 -17
- package/lib/types/client/write.d.ts +21 -8
- package/lib/types/deps.d.ts +18 -7
- package/lib/types/domain/closure.d.ts +44 -0
- package/lib/types/index.d.ts +6 -2
- package/lib/types/log/session-log.d.ts +4 -3
- package/lib/types/log/zstd-frames.d.ts +13 -8
- package/lib/types/ports/agent-handles.d.ts +1 -1
- package/lib/types/ports/connection-port.d.ts +33 -0
- package/lib/types/ports/fs-port.d.ts +24 -9
- package/lib/types/ports/host-port.d.ts +21 -50
- package/lib/types/ports/presence-port.d.ts +31 -0
- package/lib/types/shared/wire.d.ts +175 -38
- package/lib/types/trash/attachments.d.ts +92 -0
- package/lib/types/trash/detail.d.ts +46 -16
- package/lib/types/trash/entry.d.ts +10 -4
- package/lib/types/trash/groups.d.ts +9 -11
- package/lib/types/trash/manifest.d.ts +4 -4
- package/lib/types/trash/storage-manifest.d.ts +1 -1
- package/lib/types/verbs/batch-carried.d.ts +1 -3
- package/lib/types/verbs/delete-session.d.ts +2 -2
- package/lib/types/verbs/errors.d.ts +2 -0
- package/lib/types/verbs/live-admission.d.ts +4 -7
- package/lib/types/verbs/member-journal.d.ts +3 -4
- package/lib/types/verbs/member-presence.d.ts +16 -37
- package/lib/types/verbs/move-group.d.ts +2 -1
- package/lib/types/verbs/orphan-subagents.d.ts +19 -12
- package/lib/types/verbs/permanent-removal.d.ts +60 -25
- package/lib/types/verbs/restore-session.d.ts +7 -13
- package/lib/types/verbs/trash-scope.d.ts +36 -0
- package/package.json +117 -106
- package/lib/types/verbs/purge-trash.d.ts +0 -30
|
@@ -16,7 +16,7 @@ export interface JournalUndo {
|
|
|
16
16
|
export interface JournalStep {
|
|
17
17
|
/** 稳定的领域键,给 `restored(key)` 用。 */
|
|
18
18
|
readonly key: JournalStepKey;
|
|
19
|
-
/** 这一步在做什么。失败原文按「`<label
|
|
19
|
+
/** 这一步在做什么。失败原文按「`<label>:<原错误>`」拼。 */
|
|
20
20
|
readonly label: string;
|
|
21
21
|
/** 做这一步。抛什么都由 journal 折成 `sessiondelete/io`(除非它自己已经是一个动词码)。 */
|
|
22
22
|
run(): Promise<void>;
|
|
@@ -39,8 +39,7 @@ export declare function attempt(label: string, run: () => Promise<unknown>): Pro
|
|
|
39
39
|
export declare function stepUndo(label: string, run: () => Promise<unknown>): Promise<StepUndo>;
|
|
40
40
|
/**
|
|
41
41
|
* **组级回滚**:把已经做成的成员按相反顺序退回;某一步退不干净时把失败并进原错误的
|
|
42
|
-
* `details.rollbackFailures
|
|
43
|
-
*
|
|
44
|
-
* "相反顺序 + 失败合并"不再各写一遍。
|
|
42
|
+
* `details.rollbackFailures`,绝不让原错误独走。那些失败句子的合并与措辞住 `src/verbs/errors.ts`;
|
|
43
|
+
* 删除与恢复两个按组走的动词共用这一处。
|
|
45
44
|
*/
|
|
46
45
|
export declare function rollbackJournals(journals: readonly Journal[], error: unknown): Promise<never>;
|
|
@@ -1,42 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 成员离场 / 归位的通告协议:摘活体、三态判断、成对事件只在这里做。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 六处调用曾经各写一遍
|
|
6
|
-
* `evictLive → !== 'unknown' → announceRemoved`;删除回滚还要自己拼目录与账本两个布尔
|
|
7
|
-
* 才发 `announceRestored`。
|
|
8
|
-
* 这条协议说错一次就是客户端留着假行、或删掉真行,所以只留一个家。
|
|
4
|
+
* 调用方包括 delete、restore 回滚、purge、orphan-clean;协议说错一次就是客户端留假行或删真行。
|
|
9
5
|
*
|
|
10
6
|
* 边界:
|
|
11
|
-
* -
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* - 本模块用到的三条(`evictLive` / `announceRemoved` / `announceRestored`)按契约不抛(evictLive 三态、
|
|
16
|
-
* announce* 内部吞异常),这里不再叠 catch。
|
|
7
|
+
* - 批量恢复的编排归 `verbs/batch.ts`(ADR-0002);本模块只实现 `announceRestored(ids)`(ADR-0019)。
|
|
8
|
+
* - 哪个键算归位由动词在 `tail.onUndone` 里读成事实再交进来,真值表住这里;
|
|
9
|
+
* `run`/`undo` 之后补通告的形状由 {@link withPresenceTail} 拥有。
|
|
10
|
+
* - `evictLive`/`announce*` 按契约不抛,这里不再叠 catch。
|
|
17
11
|
*/
|
|
18
12
|
import type { HostPresencePort } from '../ports/host-port.js';
|
|
13
|
+
import type { PresenceCapability } from '../ports/presence-port.js';
|
|
19
14
|
import type { Journal, JournalUndo } from './member-journal.js';
|
|
20
15
|
/** 本模块实际用到的三条宿主能力。`Pick` 此刻覆盖 `HostPresencePort` 的三条方法,仍留着它当闸:
|
|
21
|
-
*
|
|
22
|
-
* 自己发起,不该因为共用同一颗 adapter 就在类型上声明依赖。 */
|
|
16
|
+
* 将来端口多出第四条时,这里不会被动跟着变宽——能力面只暴露这三条,批量通告的编排归 `verbs/batch.ts`。 */
|
|
23
17
|
export type MemberPresenceHost = Pick<HostPresencePort, 'evictLive' | 'announceRemoved' | 'announceRestored'>;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
export interface MemberPresence {
|
|
31
|
-
/** 成员已经不在宿主可见列表:摘活体;三态不是 unknown 时发 removed。
|
|
32
|
-
* `backInTrash: false` 表示目录没退回回收站(恢复回滚失败),此时什么都不做。 */
|
|
33
|
-
memberGone(sessionId: string, facts?: {
|
|
34
|
-
readonly backInTrash?: boolean;
|
|
35
|
-
}): Promise<void>;
|
|
36
|
-
/** 成员已经回到原来的可见状态:目录与账本两件写都回去才发 added。 */
|
|
37
|
-
memberBack(sessionId: string, facts: MemberBackFacts): Promise<void>;
|
|
38
|
-
}
|
|
39
|
-
export declare function createMemberPresence(host: MemberPresenceHost): MemberPresence;
|
|
18
|
+
/** 本仓的在场能力实现,也是**全仓唯一的盖章处**:端口形状住 `src/ports/presence-port.ts`,这里只做
|
|
19
|
+
* 三态与真值表。品牌字段是类型层的(unique symbol 不导出、没有运行时值),所以只有这一处显式断言
|
|
20
|
+
* 能把三件方法盖成 `PresenceCapability`——手拼同样的三件满足不了那个面(`tests/deps.test.ts` 钉住)。
|
|
21
|
+
* `announceRestored` 直接交宿主那一手:能力面与宿主面同名同签名,再包一层只会多一个转调。 */
|
|
22
|
+
export declare function createMemberPresence(host: MemberPresenceHost): PresenceCapability;
|
|
40
23
|
/** 一次 undo 之后要发哪一条通告:`leave` = 成员已不在可见面,`return` = 已回到原可见状态。 */
|
|
41
24
|
export type PresenceAnnounce = {
|
|
42
25
|
readonly kind: 'leave';
|
|
@@ -55,13 +38,9 @@ export interface PresenceTail {
|
|
|
55
38
|
onUndone(undo: JournalUndo): PresenceAnnounce;
|
|
56
39
|
}
|
|
57
40
|
/**
|
|
58
|
-
* 给一次成员移动的 journal 补上「在场那条尾巴」:run
|
|
59
|
-
*
|
|
60
|
-
* 为什么是个工厂:删除与恢复此前各手拼一个匿名 journal,形状逐字相同(`journal.run` 加上
|
|
61
|
-
* `presence.memberGone`/`journal.undo` 加上 `presence.memberBack`),只差键 → 事实的映射。
|
|
62
|
-
* 两处各写一遍,就是「补在哪一拍」这条顺序又有第二个家。
|
|
41
|
+
* 给一次成员移动的 journal 补上「在场那条尾巴」:run 完全成功之后离场,undo 之后按事实归位或离场。
|
|
63
42
|
*
|
|
64
|
-
* 边界:`memberGone` 的 `backInTrash:false` 早退与 `memberBack`
|
|
65
|
-
*
|
|
43
|
+
* 边界:`memberGone` 的 `backInTrash:false` 早退与 `memberBack` 的两件齐备真值表归 `createMemberPresence`;
|
|
44
|
+
* 这里只把事实原样递过去。
|
|
66
45
|
*/
|
|
67
|
-
export declare function withPresenceTail(presence:
|
|
46
|
+
export declare function withPresenceTail(presence: PresenceCapability, journal: Journal, tail: PresenceTail): Journal;
|
|
@@ -12,7 +12,8 @@ export interface GroupMove<M, R> {
|
|
|
12
12
|
readonly root: M;
|
|
13
13
|
/** 跟随者:按序跑,每个都拿到根结果(彼此看不到对方的结果)。 */
|
|
14
14
|
readonly followers: readonly M[];
|
|
15
|
-
move
|
|
15
|
+
/** 属性声明:`move` 会被解构出来按序调用(实现不读 `this`),方法简写会带隐式 `this: GroupMove`。 */
|
|
16
|
+
move: (member: M, rootResult: R | undefined) => Promise<{
|
|
16
17
|
journal: Journal;
|
|
17
18
|
result: R;
|
|
18
19
|
}>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SessionIdentity } from '../ports/host-port.js';
|
|
2
2
|
import type { TrashEntry } from '../trash/entry.js';
|
|
3
3
|
import type { OrphanCleanStatsWire, OrphanReportWire } from '../shared/wire.js';
|
|
4
|
-
import type { CoreDeps } from '../deps.js';
|
|
4
|
+
import type { CoreDeps, RemovalDeps } from '../deps.js';
|
|
5
5
|
/** 组里一个人的盘上身份(字节是**扫描那一刻**量的,清理时按目录重量一次,不拿这份快照冒充准数)。 */
|
|
6
6
|
export interface OrphanMember {
|
|
7
7
|
readonly sessionId: string;
|
|
@@ -27,22 +27,28 @@ interface RawGroup {
|
|
|
27
27
|
* 彻底删除(组语义见 `src/trash/groups.ts`),把它的根当孤儿删掉就是**替用户做了一个没人点过的决定**。孤儿只
|
|
28
28
|
* 有一种来路:父被彻底删掉了(或被手工从盘上清掉了),清册里再也没有这一行。
|
|
29
29
|
*
|
|
30
|
-
* 根按**传递闭包**收全部后代(与 `HostCorpusPort.subagentMembersOf`
|
|
31
|
-
*
|
|
30
|
+
* 根按**传递闭包**收全部后代(与 `HostCorpusPort.subagentMembersOf` 同一实现:闭包核心住
|
|
31
|
+
* `src/domain/closure.ts` 的 `orphanRootsOf`,孩子与孙代都算,深度不设上限),同一条记录只归一组
|
|
32
|
+
* (`claimed`)——数据里真出现环时结果是"少数一条",不是把一个会话删两遍。
|
|
32
33
|
* 归堆与去重**两边都去 `session-` 前缀**:同一个父在两代宿主里写法不同(见 `bareSessionId` 的来历)。
|
|
33
34
|
*/
|
|
34
35
|
export declare function orphanGroupsOf(corpus: readonly SessionIdentity[], trashSessionIds: readonly string[]): RawGroup[];
|
|
35
|
-
/** **本函数是纯扫描,不对账、不写清册行**(`/orphans` 声明为 pure
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
/** **本函数是纯扫描,不对账、不写清册行**(`/orphans` 声明为 pure,一次扫描不能顺手写行)。
|
|
37
|
+
* 清册行由调用方选好再交进来:`/orphans` 交 `read.rows()`(raw 索引),`orphan-clean` 交
|
|
38
|
+
* `ledger.list()`(对账后的行)——否则刚被人手工搬进 `session-trash/` 的父目录还没有行,会被当成
|
|
39
|
+
* "父没了"直接删盘。扫描不再自己回落到 `ledger.raw()`:读面归属住路由记录(ADR-0004 / ADR-0012)。
|
|
39
40
|
*
|
|
40
41
|
* `listSessions()` 回 `{ available: false }` 时**必须抛**:那是宿主的会话语料服务没挂,不是"这个家没有孤儿"。
|
|
41
42
|
* 拿缺席当空语料 ⇒ 报告说"零条孤儿"、界面上一句"没有要清理的",而盘上真有几百 MB——那是假话,
|
|
42
|
-
* 所以这里按 `io` 响亮失败(AGENTS.md:`available: false` 意味着服务缺席,扫描方不许冒充读数)。
|
|
43
|
+
* 所以这里按 `io` 响亮失败(AGENTS.md:`available: false` 意味着服务缺席,扫描方不许冒充读数)。
|
|
44
|
+
*
|
|
45
|
+
* `roots` 只收请求点名的那几组、**在量字节之前过滤**:`/orphans` 不传(报告全部),
|
|
46
|
+
* `orphan-clean` 传这次的根——否则点一组会把整个家所有孤儿的树都递归量一遍。 */
|
|
43
47
|
export declare function loadOrphanGroups(deps: CoreDeps,
|
|
44
|
-
/**
|
|
45
|
-
trash
|
|
48
|
+
/** 调用方选好并交来的清册行:`/orphans` 交 `read.rows()`,`orphan-clean` 交 `ledger.list()`。 */
|
|
49
|
+
trash: readonly TrashEntry[],
|
|
50
|
+
/** 只要这些根(两边都去 `session-` 前缀再比);缺席 = 全量扫描。 */
|
|
51
|
+
roots?: readonly string[]): Promise<readonly OrphanGroup[]>;
|
|
46
52
|
/** 把一组组收成那份报告:根清单 + 闭包总数/总字节 + 失踪父的**去重计数** + 按项目目录的分摊。
|
|
47
53
|
* `groups` 按字节从多到少排(界面上"哪家还剩得最多"要看第一行)。 */
|
|
48
54
|
export declare function reportOrphans(groups: readonly OrphanGroup[]): OrphanReportWire;
|
|
@@ -58,6 +64,7 @@ export declare function reportOrphans(groups: readonly OrphanGroup[]): OrphanRep
|
|
|
58
64
|
* - `unarchive` 对未归档的会话是官方幂等 no-op(`registry.unarchiveSession`),所以这里不需要那次
|
|
59
65
|
* `wasArchived` 读——这里没有"还原到删除前"的义务。
|
|
60
66
|
* - 摘账本只在账本真记着它时做(`workspaceOf` 未命中 = 本来就是「未分组」,不是故障)。
|
|
67
|
+
* - 独占图片的剩余引用源由附件域自己扫:活动会话 + 全部 session-trash + storages/cache。
|
|
61
68
|
*/
|
|
62
|
-
export declare function cleanOrphanGroup(deps:
|
|
69
|
+
export declare function cleanOrphanGroup(deps: RemovalDeps, group: OrphanGroup): Promise<OrphanCleanStatsWire>;
|
|
63
70
|
export {};
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* 永久删除的测量、计划与执行:先记录目录/字节/账本归属,再按计划动盘。
|
|
3
|
+
* 计划函数只读;活体准入在计划阶段、任何写之前完成,执行器动第一笔写前再查一次。
|
|
4
|
+
*
|
|
5
|
+
* 测量与补账本归属**分家**:`/list` 的只读计划只吃 {@link measureMembers}(零账本读,不再为每个
|
|
6
|
+
* 盘上后代问一次 `workspaceOf`,`/list` 的失败面不再含账本归属读);`workspaceOf` 只有
|
|
7
|
+
* purge / orphan-clean 的删除计划({@link planMemberRemovals})才付——executor 摘账本要用它。
|
|
5
8
|
*/
|
|
6
9
|
import { type TrashEntry } from '../trash/entry.js';
|
|
7
10
|
import type { TrashGroupView } from '../trash/groups.js';
|
|
8
|
-
import type
|
|
11
|
+
import { type ImageRemovalPlan } from '../trash/attachments.js';
|
|
12
|
+
import type { CoreDeps, RemovalDeps } from '../deps.js';
|
|
13
|
+
import { type AttachmentCleanupWire } from '../shared/wire.js';
|
|
9
14
|
export interface MeasuredMemberDir {
|
|
10
15
|
readonly dir: string;
|
|
11
16
|
readonly bytes: number;
|
|
12
17
|
}
|
|
13
|
-
/** 一条成员被删之前要记下的三件:目录、字节、账本归属(缺席 =
|
|
14
|
-
*
|
|
18
|
+
/** 一条成员被删之前要记下的三件:目录、字节、账本归属(缺席 = 未分组)。purge / orphan-clean 的
|
|
19
|
+
* 删除计划共用这一份形状;`/list` 的只读计划不吃它——那一面零账本读,见 {@link measureMembers}。 */
|
|
15
20
|
export interface PlannedMemberRemoval {
|
|
16
21
|
readonly sessionId: string;
|
|
17
22
|
readonly dir: string;
|
|
@@ -24,13 +29,21 @@ export declare function measureMemberDir(deps: CoreDeps, member: {
|
|
|
24
29
|
readonly sessionId: string;
|
|
25
30
|
readonly projectDir: string;
|
|
26
31
|
}): Promise<MeasuredMemberDir | undefined>;
|
|
27
|
-
/**
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
/** 一条成员的**测量**读数:身份两格 + 目录 + 实测字节。没有账本归属那一格——归属是删除动词才要
|
|
33
|
+
* 读的账本事实(见 {@link measureMembers} 与 {@link PlannedMemberRemoval} 的分工)。 */
|
|
34
|
+
export interface MeasuredMember {
|
|
35
|
+
readonly sessionId: string;
|
|
36
|
+
readonly projectDir: string;
|
|
37
|
+
readonly dir: string;
|
|
38
|
+
readonly bytes: number;
|
|
39
|
+
}
|
|
40
|
+
/** 只测量(目录 + 实测字节):`/list` 的只读计划只吃这一支,零账本读。目录不在的成员跳过
|
|
41
|
+
* (记录与盘不一致是那条成员自己的事)。为什么不顺手把归属也问了:`/list` 投影只用字节与条数,
|
|
42
|
+
* 那一格立刻被丢弃——每开一次回收站为它付 N 次宿主调用,还把账本归属读拉进 `/list` 的失败面。 */
|
|
43
|
+
export declare function measureMembers(deps: CoreDeps, members: readonly {
|
|
31
44
|
readonly sessionId: string;
|
|
32
45
|
readonly projectDir: string;
|
|
33
|
-
}[]): Promise<
|
|
46
|
+
}[]): Promise<MeasuredMember[]>;
|
|
34
47
|
/** 回收站里的一条要删的行:目录、在不在、实测字节(缺席 = 0)。purge 与 `/list` 都由它量。 */
|
|
35
48
|
export interface PlannedTrashRow {
|
|
36
49
|
readonly entry: TrashEntry;
|
|
@@ -38,10 +51,11 @@ export interface PlannedTrashRow {
|
|
|
38
51
|
readonly present: boolean;
|
|
39
52
|
readonly bytes: number;
|
|
40
53
|
}
|
|
41
|
-
/** `/list` 的一行(一组)所需的只读数:`releaseBytes` 是这一组**实测**会释放的字节。
|
|
54
|
+
/** `/list` 的一行(一组)所需的只读数:`releaseBytes` 是这一组**实测**会释放的字节。
|
|
55
|
+
* `members` 是 {@link MeasuredMember}(测量读数,无账本归属格):清单只用它的字节与条数。 */
|
|
42
56
|
export interface VisibleTrashGroup extends TrashGroupView {
|
|
43
57
|
readonly trash: readonly PlannedTrashRow[];
|
|
44
|
-
readonly members: readonly
|
|
58
|
+
readonly members: readonly MeasuredMember[];
|
|
45
59
|
readonly releaseBytes: number;
|
|
46
60
|
readonly subagentBytes: number;
|
|
47
61
|
readonly strandedBytes: number;
|
|
@@ -52,7 +66,8 @@ export interface VisibleTrashGroup extends TrashGroupView {
|
|
|
52
66
|
* 不用清册里记的 `sizeBytes`(那是删除那一刻的事实,盘上早就变了)。
|
|
53
67
|
*
|
|
54
68
|
* 盘上后代经 `selectStrandedMembers` 选出(排除全部清册 id、bare id 全局只算一次);目录不在的成员
|
|
55
|
-
* 不进 `members`(与
|
|
69
|
+
* 不进 `members`(与 purge 计划同口径)。成员只吃 {@link measureMembers}:**零账本读**——不再为每个
|
|
70
|
+
* 盘上后代问一次 `workspaceOf`,`/list` 的失败面不再含账本归属读(归属只有 purge 摘账本才付)。
|
|
56
71
|
*/
|
|
57
72
|
export declare function planVisibleTrash(deps: CoreDeps, rows: readonly TrashEntry[], groups: readonly TrashGroupView[]): Promise<VisibleTrashGroup[]>;
|
|
58
73
|
/** 一次永久删除的只读计划:哪些目录要删、各自实测多少字节、哪些会话要先摘归档集。
|
|
@@ -66,17 +81,24 @@ export interface RemovalPlan {
|
|
|
66
81
|
readonly unarchive: readonly string[];
|
|
67
82
|
/** 计划量到的合计;成功回执的 freedBytes 只念它。 */
|
|
68
83
|
readonly bytes: number;
|
|
84
|
+
/** 这一次要连带清掉的独占图片对象(`trash/attachments.ts` 的域判定;空计划 = 没有可删的、或
|
|
85
|
+
* 剩余引用源读不出而整体跳过——原因在 `skipped` 里)。附件字节**不进** `bytes`:那是会话目录的
|
|
86
|
+
* 释放量,附件读数单独走回执的 `attachments`。 */
|
|
87
|
+
readonly attachments: ImageRemovalPlan;
|
|
69
88
|
}
|
|
70
89
|
/**
|
|
71
90
|
* purge 的删除计划:`targets` 由调用方按组语义(`trash/groups.ts`)选好,这里只量、只筛、只查活体。
|
|
72
|
-
*
|
|
91
|
+
* **计划函数是执行前的第一道闸**——调用方拿到计划才允许写;计划与第一笔写之间的窗口由
|
|
92
|
+
* `executeRemoval` 在首笔写前复查(见该函数头注)。
|
|
73
93
|
*
|
|
74
94
|
* - `trash` 按 targets 原序逐个实测(目录缺席的行 `present:false` / 0 字节,但清册行仍要摘)。
|
|
75
95
|
* - 盘上后代经 `selectStrandedMembers` 选出(一次问全部 targets、排除全部清册 id、bare id 全局只计划一次)。
|
|
76
96
|
* - `members` 只收盘上还有目录的成员(目录缺席没有可删的字节);`unarchive` = targets + 这些成员:
|
|
77
97
|
* 只摘这一次真要碰的 id。
|
|
98
|
+
* - `attachments` 由被删目录(targets + 这些成员)里的图片引用与全部剩余引用源算独占;共享的保留、
|
|
99
|
+
* 剩余引用源读不出就整体跳过(判据住 `trash/attachments.ts`,这里只接线)。
|
|
78
100
|
* - 活体准入在量目录与任何写之前逐个查 targets 与**全部枚举到的**盘上后代;任一在跑就整条拒绝、
|
|
79
|
-
*
|
|
101
|
+
* 零副作用。没有跳过它的开关;执行器首笔写前还会用计划里的 `unarchive` 名单复查(ADR-0027)。
|
|
80
102
|
* - `sessionQuery` 缺席时端口回 `{ available: false }` ⇒ members 为空、不抛(盘上残留留给 `GET /orphans` 兜底)。
|
|
81
103
|
*/
|
|
82
104
|
export declare function planTrashRemoval(deps: CoreDeps, rows: readonly TrashEntry[], targets: readonly TrashEntry[]): Promise<RemovalPlan>;
|
|
@@ -87,6 +109,8 @@ export declare function planTrashRemoval(deps: CoreDeps, rows: readonly TrashEnt
|
|
|
87
109
|
* - `unarchive` 覆盖**全部传入成员**(含目录缺席):orphan-clean 在删目录之前整批摘归档集,
|
|
88
110
|
* 目录缺席的 id 也可能还挂在归档集里;重试时不会再长出一条幽灵 id。
|
|
89
111
|
* - live 准入覆盖**全部传入成员**(含目录缺席):它们都在这一组的可见面里,任一在跑就整组拒绝。
|
|
112
|
+
* - `attachments` 的剩余引用源由附件域自己扫(活动会话 + 全部 session-trash + storages/cache),
|
|
113
|
+
* 共享图片因此保留(判据住 `trash/attachments.ts`)。
|
|
90
114
|
* - 回执由 `kind:'orphan'` 派生:删目录阶段失败时执行器要交回 `{removed, freedBytes, failedSessionId}`。
|
|
91
115
|
*/
|
|
92
116
|
export declare function planOrphanRemoval(deps: CoreDeps, members: readonly {
|
|
@@ -94,21 +118,32 @@ export declare function planOrphanRemoval(deps: CoreDeps, members: readonly {
|
|
|
94
118
|
readonly projectDir: string;
|
|
95
119
|
}[]): Promise<RemovalPlan>;
|
|
96
120
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
121
|
+
* 按计划执行一次永久删除:不再量。回执的字节念计划(`plan.bytes`),执行期不再累加一遍。
|
|
122
|
+
*
|
|
123
|
+
* **计划 + 执行器、准入查两遍**(ADR-0027):计划期准入证明计划成立时没有会话在跑;执行器首笔写前
|
|
124
|
+
* 再用同一份名单复查,补上附件扫描撑开的窗口。
|
|
125
|
+
*
|
|
126
|
+
* 计划期准入与第一笔写之间还隔着附件引用的全 home 扫描(`planImageAttachmentRemoval`),大 home
|
|
127
|
+
* 上那是秒级窗口——期间用户 resume 一个会话,它的目录随后仍会被删。所以动任何写之前用计划里同一份
|
|
128
|
+
* 会话名单**再查一次活体**(只读、廉价);这一查失败仍然零写入。名单取 `plan.unarchive`:
|
|
129
|
+
* 它覆盖所有会被删目录的会话(回收站行与盘上成员),目录已不在的成员没有可删的东西。
|
|
130
|
+
*
|
|
131
|
+
* 顺序:整批摘归档集 → 摘有账本的成员的账本 → 逐条回收站行
|
|
132
|
+
* (在才删目录 → 摘清册行 → 通告成员离场)→ 逐条盘上成员(删目录 → 通告成员离场)→ 逐条附件对象。
|
|
133
|
+
* 目录删掉就没有"退回去"这回事,中途失败**不回滚**;已经删掉的由下面的计数器如实交回。
|
|
99
134
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* "退回去"这回事,中途失败**不回滚**;已经删掉的由下面的计数器如实交回。
|
|
135
|
+
* 附件清理排在**所有会话目录删完之后**:对象只在会话真的没了之后才动;单文件失败只进
|
|
136
|
+
* `attachments.failed`,不影响(也不回滚)会话删除的回执。
|
|
103
137
|
*
|
|
104
|
-
*
|
|
138
|
+
* 失败形状:
|
|
105
139
|
* - `kind:'orphan'`(orphan-clean)⇒ `io` + `{removed, freedBytes, failedSessionId}`,不伪装成整组没动;
|
|
106
|
-
* - `kind:'trash'`(purge)⇒ 同一个标签、不带 details
|
|
107
|
-
* - orphan-clean 的 unarchive / detach
|
|
140
|
+
* - `kind:'trash'`(purge)⇒ 同一个标签、不带 details;
|
|
141
|
+
* - orphan-clean 的 unarchive / detach 原样抛出。
|
|
108
142
|
*
|
|
109
143
|
* 通告只经 `member-presence`(摘活体三态 + `api-session/removed`),不在这里另写一份。
|
|
110
144
|
*/
|
|
111
|
-
export declare function executeRemoval(deps:
|
|
145
|
+
export declare function executeRemoval(deps: RemovalDeps, plan: RemovalPlan): Promise<{
|
|
112
146
|
removed: number;
|
|
113
147
|
freedBytes: number;
|
|
148
|
+
attachments: AttachmentCleanupWire;
|
|
114
149
|
}>;
|
|
@@ -2,21 +2,15 @@ import { type TrashEntry } from '../trash/entry.js';
|
|
|
2
2
|
import { type HostIdentityPort } from '../ports/host-port.js';
|
|
3
3
|
import type { CoreDeps } from '../deps.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* 只回父就会在回收站里留下一堆点不到的孩子行——清单只列父行,那些行既看不见也清不掉。
|
|
5
|
+
* 恢复:把删除那次按原路退回,并按组走。
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* 「未分组」收(判据在 {@link attachableWorkspaceId}),照旧回盘——恢复是加法,不该因为一个可选的账目而失败。
|
|
14
|
-
* ② **逐个恢复**,顺序与删除相反:文件先回来 → 挂账本 →(旧清单迁移)取消归档 → 摘清册行。
|
|
15
|
-
* ③ 中途失败时 journal 把这个成员自己退回回收站;**已经恢复完的**兄弟由整组 catch 按相反顺序一起退回。
|
|
7
|
+
* 三段:
|
|
8
|
+
* 1. 预检整组(不写盘):每个成员的回收站目录与原项目目录都必须在;工作区账本缺席不算硬失败,
|
|
9
|
+
* 恢复后归入「未分组」。预检在任何写之前,避免留下半组。
|
|
10
|
+
* 2. 逐个恢复:文件先回来 → 挂账本 →(旧条目)取消归档 → 摘清册行。
|
|
11
|
+
* 3. 中途失败由 journal 回滚当前成员;已恢复的兄弟由整组按相反顺序退回。
|
|
16
12
|
*
|
|
17
|
-
*
|
|
18
|
-
* "恢复很慢"就是逐条发)。通告归 `verbs/batch.ts`(ADR-0002),所以这个动词的 interface 是
|
|
19
|
-
* "一个条目 id + 这一组进、回收站那一行出",没有批量专用的开关。
|
|
13
|
+
* 通告不在本文件:批量层在批尾一次 `announceRestored(ids)`(ADR-0002)。
|
|
20
14
|
*/
|
|
21
15
|
export declare function restoreSession(deps: CoreDeps): (entryId: string,
|
|
22
16
|
/** 这一组(只读):批量层由 `batch-carried.ts` 的 `claimOf` 给出,直调者按 `trash/groups.ts`
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { TrashEntry } from '../trash/entry.js';
|
|
2
|
+
import type { RemovalDeps } from '../deps.js';
|
|
3
|
+
import type { PurgeStatsWire } from '../shared/wire.js';
|
|
4
|
+
import { type OrphanGroup } from './orphan-subagents.js';
|
|
5
|
+
/**
|
|
6
|
+
* **清册作用域(trash scope)**:purge 这条写路径的「拿目标」owner。调用方把一份选好读面的清册
|
|
7
|
+
* 快照交进来(写入口 = 真相读;报告入口不经作用域——`/orphans` 直接把 raw 快照交 `loadOrphanGroups`。
|
|
8
|
+
* 选哪一格是路由行 / 批量层的事,ADR-0012 的边界不在这里翻),快照与**收缩视图**从此归本模块、
|
|
9
|
+
* **不外露**:三态判据与按组收缩都仍读 `trash/groups.ts` 的 `claimOf` / `withoutGroup`,这里只**持有**
|
|
10
|
+
* 那份视图,不抄第二遍判据。三个成员的输入输出以 `CONTEXT.md` 的「清册作用域」与下面各成员的
|
|
11
|
+
* 注释为准,头注不重写一遍表面契约。
|
|
12
|
+
*
|
|
13
|
+
* 彻底删除:从回收站里**真的**抹掉一组(或整站),并连带那些"还留在盘上、没被带走"的后代。
|
|
14
|
+
* **这里只做两件事:拿目标、把计划交给执行器**。量目录、筛盘上后代、活体准入与"先退归档集、
|
|
15
|
+
* 再摘账本、最后删目录"的顺序都住 `verbs/permanent-removal.ts`——计划只读,执行器不再量、只在第一笔
|
|
16
|
+
* 写前补一次只读的活体复查,计划是执行前的第一道闸(别在这里再长出一段手写步骤)。
|
|
17
|
+
*
|
|
18
|
+
* **不做回滚**:目录删掉就没有"退回去"这回事(删到一半失败时已删目录不回滚;带部分计数的
|
|
19
|
+
* `details` 回执只属于 `orphan-clean`,purge 的 `removed` / `freedBytes` 仍只在成功回执里给)。
|
|
20
|
+
* 对应地,`kind:'trash'` 的计划失败时执行器只抛那一步的 `io` 与原文、不带 details。
|
|
21
|
+
*
|
|
22
|
+
* 回执里的 `attachments` 原样来自执行器:独占图片在会话目录删完之后才清,附件失败只计数、不改
|
|
23
|
+
* `removed` / `freedBytes`(`skipped` = 有候选或剩余引用源读不出、这一轮部分或整体没清,原因上屏)。
|
|
24
|
+
* 「已被同批前一条带走」的幂等回执**不带** `attachments`——那一趟没有附件读数,缺席与 0 是两件事,
|
|
25
|
+
* 不拿零冒充"清过"(与旧批量回执同形)。
|
|
26
|
+
*/
|
|
27
|
+
export interface TrashScope {
|
|
28
|
+
/** purge 这一组;已被同批带走 ⇒ `{removed:0,freedBytes:0}`;absent(原始快照里就认不出)⇒
|
|
29
|
+
* `trash-empty`,原文点名 id。 */
|
|
30
|
+
purge(id: string): Promise<PurgeStatsWire>;
|
|
31
|
+
/** 清空:全部行一组。 */
|
|
32
|
+
purgeAll(): Promise<PurgeStatsWire>;
|
|
33
|
+
/** 点名根的无主组(根两边去 `session-` 前缀再比);roots 缺席 = 全量。 */
|
|
34
|
+
orphanGroups(roots?: readonly string[]): Promise<readonly OrphanGroup[]>;
|
|
35
|
+
}
|
|
36
|
+
export declare function createTrashScope(deps: RemovalDeps, rows: readonly TrashEntry[]): TrashScope;
|
package/package.json
CHANGED
|
@@ -1,106 +1,117 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@xrn1997/dsh-session-delete",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "在 DeepSeek Harness 里删除会话:删除=移入回收站(可恢复),另设显式彻底删除",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"./
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
"lib/
|
|
24
|
-
"lib/
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"@
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"@
|
|
52
|
-
"@
|
|
53
|
-
"@
|
|
54
|
-
"
|
|
55
|
-
"react": "^
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
},
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"
|
|
105
|
-
|
|
106
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@xrn1997/dsh-session-delete",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "在 DeepSeek Harness 里删除会话:删除=移入回收站(可恢复),另设显式彻底删除",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@11.25.0",
|
|
7
|
+
"main": "lib/index.js",
|
|
8
|
+
"types": "lib/types/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./lib/types/index.d.ts",
|
|
12
|
+
"default": "./lib/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./client": {
|
|
15
|
+
"types": "./lib/types/client/index.d.ts",
|
|
16
|
+
"default": "./lib/client.js"
|
|
17
|
+
},
|
|
18
|
+
"./locale/*.json": "./locale/*.json",
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"icon": "./icon.svg",
|
|
22
|
+
"files": [
|
|
23
|
+
"lib/index.js",
|
|
24
|
+
"lib/client.js",
|
|
25
|
+
"lib/types/**/*.d.ts",
|
|
26
|
+
"locale",
|
|
27
|
+
"icon.svg",
|
|
28
|
+
"cordis.patch.yml",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=24",
|
|
34
|
+
"dsh": ">=0.1.7-rc.2 <0.3.0-0"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsdown && tsc -p tsconfig.build.json",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"lint": "eslint .",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"pack:dry": "npm pack --dry-run --ignore-scripts",
|
|
43
|
+
"prepack": "npm run build",
|
|
44
|
+
"gates": "pnpm typecheck && pnpm lint && pnpm test && pnpm build && pnpm run pack:dry"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
48
|
+
"react": "^18.3.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
52
|
+
"@eslint/js": "^10.0.1",
|
|
53
|
+
"@testing-library/dom": "^10.4.2",
|
|
54
|
+
"@testing-library/jest-dom": "^6.10.0",
|
|
55
|
+
"@testing-library/react": "^16.3.3",
|
|
56
|
+
"@types/node": "24.19.0",
|
|
57
|
+
"@types/react": "18.3.31",
|
|
58
|
+
"@types/react-dom": "18.3.5",
|
|
59
|
+
"@types/semver": "^7.8.0",
|
|
60
|
+
"eslint": "^10.14.0",
|
|
61
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
62
|
+
"globals": "^17.13.0",
|
|
63
|
+
"jsdom": "^26.1.0",
|
|
64
|
+
"react": "^18.3.1",
|
|
65
|
+
"react-dom": "18.3.1",
|
|
66
|
+
"semver": "^7.8.5",
|
|
67
|
+
"tsdown": "0.23.0",
|
|
68
|
+
"typescript": "5.9.3",
|
|
69
|
+
"typescript-eslint": "^8.72.1",
|
|
70
|
+
"vitest": "3.2.7"
|
|
71
|
+
},
|
|
72
|
+
"dsh": {
|
|
73
|
+
"bundle": {
|
|
74
|
+
"patch": "./cordis.patch.yml"
|
|
75
|
+
},
|
|
76
|
+
"client": {
|
|
77
|
+
"inject": [
|
|
78
|
+
"@deepseek-ai/dsh-client-locale"
|
|
79
|
+
],
|
|
80
|
+
"platform": "web"
|
|
81
|
+
},
|
|
82
|
+
"compatibility": {
|
|
83
|
+
"dshReleases": {
|
|
84
|
+
"0.2.0-rc.2": "compatible",
|
|
85
|
+
"0.2.1-rc.1": "compatible",
|
|
86
|
+
"0.2.1-rc.2": "compatible",
|
|
87
|
+
"0.2.1-rc.3": "compatible",
|
|
88
|
+
"0.2.1-rc.5": "compatible"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"publishConfig": {
|
|
93
|
+
"access": "public"
|
|
94
|
+
},
|
|
95
|
+
"repository": {
|
|
96
|
+
"type": "git",
|
|
97
|
+
"url": "git+https://github.com/xrn1997/dsh-session-delete.git"
|
|
98
|
+
},
|
|
99
|
+
"homepage": "https://github.com/xrn1997/dsh-session-delete#readme",
|
|
100
|
+
"bugs": {
|
|
101
|
+
"url": "https://github.com/xrn1997/dsh-session-delete/issues"
|
|
102
|
+
},
|
|
103
|
+
"keywords": [
|
|
104
|
+
"dsh",
|
|
105
|
+
"deepseek-harness",
|
|
106
|
+
"plugin",
|
|
107
|
+
"session",
|
|
108
|
+
"trash",
|
|
109
|
+
"delete"
|
|
110
|
+
],
|
|
111
|
+
"author": "xrn1997",
|
|
112
|
+
"license": "Apache-2.0",
|
|
113
|
+
"dependencies": {
|
|
114
|
+
"react-markdown": "^10.1.0",
|
|
115
|
+
"remark-gfm": "^4.0.1"
|
|
116
|
+
}
|
|
117
|
+
}
|