@xrn1997/dsh-session-delete 0.6.1 → 0.7.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/README.md +18 -10
- package/THIRD_PARTY_NOTICES.md +179 -0
- package/cordis.patch.yml +1 -1
- package/docs/screenshots/screenshot-01.png +0 -0
- package/docs/screenshots/screenshot-02.png +0 -0
- package/docs/screenshots/screenshot-03.png +0 -0
- package/icon.svg +24 -18
- package/lib/client.js +1079 -245
- package/lib/index.js +206 -71
- package/lib/types/api/connection-route.d.ts +1 -1
- package/lib/types/api/dispatch.d.ts +1 -1
- package/lib/types/api/route-table.d.ts +12 -7
- package/lib/types/client/config-settings.d.ts +13 -0
- package/lib/types/client/context.d.ts +3 -0
- package/lib/types/client/copy.d.ts +34 -7
- package/lib/types/client/detail-blocks.d.ts +25 -0
- package/lib/types/client/index.d.ts +3 -2
- package/lib/types/client/maintenance-settings.d.ts +6 -7
- package/lib/types/client/remote.d.ts +32 -8
- package/lib/types/client/settings-cards.d.ts +19 -0
- package/lib/types/client/settings-view.d.ts +9 -0
- package/lib/types/client/trash-detail.d.ts +1 -1
- package/lib/types/client/trash-view.d.ts +31 -5
- package/lib/types/client/ui/icons.d.ts +23 -7
- package/lib/types/client/ui/recipe.d.ts +2 -0
- package/lib/types/client/ui/styles.d.ts +2 -0
- package/lib/types/client/use-detail-pager.d.ts +33 -0
- package/lib/types/config.d.ts +10 -11
- package/lib/types/deps.d.ts +9 -1
- package/lib/types/index.d.ts +3 -1
- package/lib/types/ports/agent-handles.d.ts +10 -2
- package/lib/types/shared/wire.d.ts +37 -5
- package/lib/types/verbs/config.d.ts +25 -0
- package/locale/en.json +1 -1
- package/locale/zh.json +6 -6
- package/package.json +20 -14
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 详情正文的**块视图词汇**:哪些块算过程内容、标准档怎么把连续过程块收成一组。
|
|
3
|
+
* 折叠状态与分页不在这里——这里只回答「这一页的块怎么读」。
|
|
4
|
+
*/
|
|
5
|
+
import type { TrashDetailBlockWire, TrashDetailInjectionBlockWire, TrashDetailTextBlockWire, TrashDetailToolBlockWire } from '../shared/wire.js';
|
|
6
|
+
/** 除用户与助手之外的块:思考、工具、注入、系统消息、压缩摘要——详情按过程内容对待。
|
|
7
|
+
* 文本块是一个 `kind` 联合的接口,`Exclude` 摘不掉其中两个成员;这里按 `kind` 收窄后并上另两种块。 */
|
|
8
|
+
export type DisclosureBlockWire = (TrashDetailTextBlockWire & {
|
|
9
|
+
kind: 'reasoning' | 'system' | 'compaction';
|
|
10
|
+
}) | TrashDetailInjectionBlockWire | TrashDetailToolBlockWire;
|
|
11
|
+
export declare function isDisclosureBlock(block: TrashDetailBlockWire): block is DisclosureBlockWire;
|
|
12
|
+
/** 这一批块里有没有对话内容(用户或助手);简洁档靠它决定还要不要继续往前翻。 */
|
|
13
|
+
export declare function hasDialogue(blocks: readonly TrashDetailBlockWire[]): boolean;
|
|
14
|
+
/** 标准档的线程单元:对话块照原位;**连续的**过程块收成一组。
|
|
15
|
+
* 组的 `id` 取它**后面那条对话块**的 id(没有对话块就 `after:end`)——prepend 更早一页只会
|
|
16
|
+
* 往组头添过程块,组的结束边界不变,已展开的组不会因为合并而收回。 */
|
|
17
|
+
export type ThreadItem = {
|
|
18
|
+
readonly kind: 'block';
|
|
19
|
+
readonly block: TrashDetailBlockWire;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'process';
|
|
22
|
+
readonly id: string;
|
|
23
|
+
readonly blocks: readonly DisclosureBlockWire[];
|
|
24
|
+
};
|
|
25
|
+
export declare function threadItems(blocks: readonly TrashDetailBlockWire[]): ThreadItem[];
|
|
@@ -6,8 +6,9 @@ export declare const name = "@xrn1997/dsh-session-delete/client";
|
|
|
6
6
|
* **`locale` 也不在这里**:宿主自己的 `ui-sidebar` 是硬注入它的,但那是"宿主知道自己那一代"的前提;
|
|
7
7
|
* 本插件要跨代活着 ⇒ 走 `ctx.get('locale')` 可选读,缺席时降级到内置 zh 文案(见 `copy.ts` 头注)。
|
|
8
8
|
* 写进 inject 就等于"这一代没有 locale 就不让整个客户端条目激活",而那等于宿主进程死。
|
|
9
|
-
* **`contextMenus` 同理**:0.2.2+ 用 `ctx.get('contextMenus')`
|
|
10
|
-
* `
|
|
9
|
+
* **`contextMenus` 同理**:0.2.2+ 用 `ctx.get('contextMenus')` 探测,服务比本条目晚到时按
|
|
10
|
+
* `ctx.inject(['contextMenus'])` 的 scoped 等待切过去;旧宿主没有这个服务时回调不来,legacy 槽位留着。
|
|
11
|
+
* 条目自己的 `inject` 仍然只有 `slots`——把可能迟到的服务写进去等于让整个客户端条目 pending。
|
|
11
12
|
*/
|
|
12
13
|
export declare const inject: string[];
|
|
13
14
|
/**
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 插件设置页的「维护」卡(ADR-0021):那张槽位上的两张卡里,本文件只拥有下面这一张。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 两段都是**手动**的:检查 → 预览 → 清理,没有挂载即扫、没有定时器。卡片的职责只有呈现:
|
|
5
5
|
* 状态那一行、检查按钮、以及各段自己的横幅 / 失败 / 提示 / 确认框;读数与动作全归
|
|
6
|
-
* `src/client/use-maintenance.ts`。
|
|
6
|
+
* `src/client/use-maintenance.ts`。两张卡的先后与 `summary` 守卫在 `src/client/settings-cards.tsx`。
|
|
7
7
|
*
|
|
8
8
|
* 与回收站面板的区别:这里没有面板高度、没有清单与详情两栏、没有全局闸。控件只用
|
|
9
|
-
* `
|
|
9
|
+
* `settingsCardShell` / `Button` / `Modal` / `fileSizeText`,所以同一张卡放在插件详情页的任意宽度下都成立。
|
|
10
10
|
*
|
|
11
11
|
* 孤儿会话段直接复用 `OrphanMaintenance`(警告条 / 失败 / 中性提示 / 预览确认的既有呈现);
|
|
12
12
|
* 孤儿附件段是本文件里的 `AttachmentMaintenance`(横幅 + 失败 + 回执 + 缩略图预览与清理确认)。
|
|
@@ -15,10 +15,9 @@ import type { ReactElement } from 'react';
|
|
|
15
15
|
import { type Translate } from './copy.js';
|
|
16
16
|
import { type MaintenanceDeps } from './use-maintenance.js';
|
|
17
17
|
export interface MaintenanceSettingsProps {
|
|
18
|
+
/** 维护四件(读数与动作);可调项两件由占用者另发给「可调项」卡。 */
|
|
18
19
|
deps: MaintenanceDeps;
|
|
19
20
|
/** 文案(缺省 = 内置 zh;宿主 locale 服务缺席时也是它,见 `copy.ts`)。 */
|
|
20
21
|
t?: Translate;
|
|
21
|
-
/** 宿主 `plugins.bundle.config` 的视图:`summary` 是插件行的一行摘要,本卡只属于详情页。 */
|
|
22
|
-
view?: 'summary' | 'page';
|
|
23
22
|
}
|
|
24
|
-
export declare function MaintenanceSettings({ deps, t
|
|
23
|
+
export declare function MaintenanceSettings({ deps, t }: MaintenanceSettingsProps): ReactElement;
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* 浏览器半的取数通道:同源 fetch 宿主 `connection.fetch` 的 exact Fetch 路由(`/api` + 插件前缀)。
|
|
3
3
|
* 信封与失败原文在这一层收口。
|
|
4
4
|
*
|
|
5
|
-
* -
|
|
5
|
+
* - 十段注册在宿主 `connection.fetch.register` 上,鉴权归宿主 `/api` bridge(ADR-0033);
|
|
6
6
|
* 没有旧前缀 fallback——支持面是 web UI 与桌面端,二者的标准组合都装 `client-connection`。
|
|
7
7
|
* 官方 remote.<namespace> 仍不可用(第三方拿不到 Typert 描述符的动态发现挂载点)。
|
|
8
|
-
* -
|
|
8
|
+
* - 十段的**段名与方法**住 `src/shared/wire.ts` 的结构化段名表(`ROUTES` 每项 `{ path, method }`),
|
|
9
9
|
* 本文件只投影段名与方法(GET 段直接取 `ROUTES.x.method`,写段把它交给 `post()`)——方法字面量
|
|
10
10
|
* 不在本文件出现第二份;取数地址(官方 `/api` 前缀 + 文档挂载点)归 `src/client/api-base.ts`
|
|
11
11
|
* (根部署下与旧版逐字相同)。
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* - error 在线上是 {code,message,details?} 普通对象;判别依据 isDSHRemoteError 住在
|
|
18
18
|
* src/shared/wire.ts,这里不 import 任何宿主客户端包。
|
|
19
19
|
*/
|
|
20
|
-
import { type ApplyAction, type ApplyOutcome, type ApplyValueByAction, type AttachmentCleanupWire, type AttachmentOrphanReportWire, type OrphanReportWire, type PurgeStatsWire, type RemoteFailure, type RemoteResult, type TrashDetailWire, type TrashRowWire } from '../shared/wire.js';
|
|
20
|
+
import { type ApplyAction, type ApplyOutcome, type ApplyValueByAction, type AttachmentCleanupWire, type AttachmentOrphanReportWire, type OrphanReportWire, type PurgeStatsWire, type RemoteFailure, type RemoteResult, type SessionDeleteConfigWire, type TrashDetailWire, type TrashRowWire } from '../shared/wire.js';
|
|
21
21
|
/**
|
|
22
22
|
* 回收站条目(`/list` 那一行)。**形状的家在 `shared/wire.ts`**(ADR-0003):这里只是别名,
|
|
23
23
|
* 所以浏览器半不再自己抄一遍字段;下面几处"只读面"的镜像同理。
|
|
@@ -57,13 +57,18 @@ export type RemoteOutcome<T> = {
|
|
|
57
57
|
*/
|
|
58
58
|
export declare function callRemote<T>(run: () => Promise<T>): Promise<RemoteOutcome<T>>;
|
|
59
59
|
/** 本插件前缀路由那套**响应信封**的窄镜像(不是宿主服务 `ctx.remote.*`——那条 typert 通道为什么用不了,见 `AGENTS.md` 的 `remote.<namespace>` 那条):
|
|
60
|
-
* 每个方法 resolve 成 `RemoteResult<T
|
|
60
|
+
* 每个方法 resolve 成 `RemoteResult<T>`。十个段名里这层走九个 JSON 段:`list` / `ping` / `config` / `orphans` / `detail` / `apply` / `empty` /
|
|
61
61
|
* `attachment-orphans` / `attachment-clean`;写动作统一走 `apply`(一组 id 进、逐条结果出);
|
|
62
62
|
* `/attachment` 是二进制段,由 `src/client/attachment-view.tsx` 直接拼地址。 */
|
|
63
63
|
export interface RawSessionDeleteRemote {
|
|
64
64
|
list(): Promise<RemoteResult<TrashEntryLike[]>>;
|
|
65
|
-
/** 通道的"在不在":一次零成本的 GET(探测专用,见 `src/api/dispatch.ts` 的 `ping` 支)。
|
|
66
|
-
|
|
65
|
+
/** 通道的"在不在":一次零成本的 GET(探测专用,见 `src/api/dispatch.ts` 的 `ping` 支)。
|
|
66
|
+
* `signal` 由探测方给:半开连接不能把 `activate` 永远吊在这里。 */
|
|
67
|
+
ping(signal?: AbortSignal): Promise<RemoteResult<string>>;
|
|
68
|
+
/** 读三只可调项的当前值:空体 POST(与 `/detail` 的“POST 表达读”同法)。 */
|
|
69
|
+
config(): Promise<RemoteResult<SessionDeleteConfigWire>>;
|
|
70
|
+
/** 写回三只可调项:走宿主 `configEditor.edit`,这一行会重启;失败以 `io` 信封回来。 */
|
|
71
|
+
saveConfig(config: SessionDeleteConfigWire): Promise<RemoteResult<SessionDeleteConfigWire>>;
|
|
67
72
|
/** 纯读:回收站之外的无主子会话扫描。它**不清理**——清理是 `apply` 的 `orphan-clean` 动作,
|
|
68
73
|
* 这一段只交回"有多少、在哪、多大"(读不到语料时报 `io`,调用方不许把它折成"零条残留")。 */
|
|
69
74
|
orphans(): Promise<RemoteResult<OrphanReportWire>>;
|
|
@@ -86,14 +91,27 @@ export interface RawSessionDeleteRemote {
|
|
|
86
91
|
* 成立(凭据是那次访问换来的 cookie,不是插件自己的秘密)。
|
|
87
92
|
*/
|
|
88
93
|
export declare function createHttpRemote(): RawSessionDeleteRemote;
|
|
94
|
+
/** 探测默认上限:5s。超时经 `AbortController` 打断 fetch,`activate` 不会永远吊在一条半开连接上。 */
|
|
95
|
+
export declare const PROBE_TIMEOUT_MS = 5000;
|
|
96
|
+
/**
|
|
97
|
+
* 探测的可选覆盖。**生产调用方不传**:注册路径只用 {@link PROBE_TIMEOUT_MS}。
|
|
98
|
+
* 这一格存在是为了让「超时算探测不过」这条支能在测试里真的跑到——不然它要等 5 秒墙上时间。
|
|
99
|
+
*/
|
|
100
|
+
export interface ProbeOptions {
|
|
101
|
+
/** 单次往返的上限;超时经 `AbortController` 打断 fetch。 */
|
|
102
|
+
readonly timeoutMs?: number;
|
|
103
|
+
}
|
|
89
104
|
/**
|
|
90
105
|
* 探测唯一通道:一次 `GET /ping` 回成功信封(`pong`)才注册入口。
|
|
91
106
|
*
|
|
92
107
|
* - 401/403 是宿主 `/api` 的纯文本身份/归属失败:入口不出现,不装禁用态。
|
|
93
108
|
* - 失败信封 / 404 / 非信封同样按不可用处理:`/api` 网关对未知路径也可能回错误形状,
|
|
94
109
|
* 不能据此把入口摆出来让每个动作都失败。
|
|
110
|
+
* - **往返有 5s 上限**:半开连接或宿主没起来时 `activate` 不悬着;超时与网络失败同样是
|
|
111
|
+
* 「这一代不可用」,不自动重试(一次探测一个结论,刷新页面才会再探)。三种失败都在
|
|
112
|
+
* console 留下原因——入口不出现是有意的,但"为什么没出现"不能只活在脑子里。
|
|
95
113
|
*/
|
|
96
|
-
export declare function probeChannel(raw: RawSessionDeleteRemote): Promise<boolean>;
|
|
114
|
+
export declare function probeChannel(raw: RawSessionDeleteRemote, options?: ProbeOptions): Promise<boolean>;
|
|
97
115
|
/** `shared/wire.ts` 的 `ApplyOutcome`、扫描报告与附件回执从这里转出去——浏览器半只认 `remote.js` 这一扇门。 */
|
|
98
116
|
export type { ApplyAction, ApplyOutcome, AttachmentCleanupWire, AttachmentOrphanReportWire, OrphanReportWire, } from '../shared/wire.js';
|
|
99
117
|
/**
|
|
@@ -107,7 +125,7 @@ export type OneShotSuccess<A extends ApplyAction> = {
|
|
|
107
125
|
readonly value: Exclude<ApplyValueByAction[A], null>;
|
|
108
126
|
};
|
|
109
127
|
/**
|
|
110
|
-
* 注入面(`deps`)的 interface
|
|
128
|
+
* 注入面(`deps`)的 interface:**九个方法**(raw 层另有 `ping` 供探测;十段里的 `/attachment` 由
|
|
111
129
|
* `src/client/attachment-view.tsx` 直接拼地址,也不在这层)。
|
|
112
130
|
*
|
|
113
131
|
* **不再有 N=1 的便捷方法**:`delete` / `restore` / `countSubagents` 曾是同一段
|
|
@@ -117,6 +135,10 @@ export type OneShotSuccess<A extends ApplyAction> = {
|
|
|
117
135
|
*/
|
|
118
136
|
export interface SessionDeleteRemote {
|
|
119
137
|
list(): Promise<TrashEntryLike[]>;
|
|
138
|
+
/** 三只可调项的当前值(`apply` 归一后的那一份)。 */
|
|
139
|
+
config(): Promise<SessionDeleteConfigWire>;
|
|
140
|
+
/** 写回三只可调项:宿主 `configEditor.edit` 落盘并重启这一行;失败就是 throw。 */
|
|
141
|
+
saveConfig(config: SessionDeleteConfigWire): Promise<SessionDeleteConfigWire>;
|
|
120
142
|
/** 回收站之外的无主子会话报告(纯读)。读不到就是 throw —— 界面上那是**一次失败的检查**,
|
|
121
143
|
* 不是"零条残留"(把 `io` 折成空报告等于把"不知道"说成"没有")。 */
|
|
122
144
|
scanOrphans(): Promise<OrphanReportWire>;
|
|
@@ -130,6 +152,8 @@ export interface SessionDeleteRemote {
|
|
|
130
152
|
/** 清空回收站(聚合回执)。 */
|
|
131
153
|
emptyTrash(): Promise<PurgeResultLike>;
|
|
132
154
|
}
|
|
155
|
+
/** 插件设置页「可调项」卡只要这两件。 */
|
|
156
|
+
export type ConfigDeps = Pick<SessionDeleteRemote, 'config' | 'saveConfig'>;
|
|
133
157
|
/** 需要那一段 `/apply` 的组件只要这一段 deps(单条非写调用见 {@link applyOne},写动作走 `./write.js` 的 `runWrite`)。 */
|
|
134
158
|
export type OneShotDeps = Pick<SessionDeleteRemote, 'applyOutcomes'>;
|
|
135
159
|
/**
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `plugins.bundle.config` 的槽位占用者:上「可调项」、下「维护」(ADR-0043 决定 4)。
|
|
3
|
+
*
|
|
4
|
+
* 本文件只有两件事:按槽位契约守住视图(`summary` 不渲染,理由见 ADR-0021),以及排两张卡的先后。
|
|
5
|
+
* 各自的呈现、状态机与读数仍归 `config-settings.tsx` 与 `maintenance-settings.tsx`。
|
|
6
|
+
*/
|
|
7
|
+
import type { ReactElement } from 'react';
|
|
8
|
+
import { type Translate } from './copy.js';
|
|
9
|
+
import type { ConfigDeps } from './remote.js';
|
|
10
|
+
import type { MaintenanceDeps } from './use-maintenance.js';
|
|
11
|
+
export interface SettingsCardsProps {
|
|
12
|
+
/** 维护四件 + 可调项两件;只造维护四件的测试替身会让「可调项」卡整体缺席。 */
|
|
13
|
+
deps: MaintenanceDeps & Partial<ConfigDeps>;
|
|
14
|
+
/** 文案(缺省 = 内置 zh;宿主 locale 服务缺席时也是它,见 `copy.ts`)。 */
|
|
15
|
+
t?: Translate;
|
|
16
|
+
/** 宿主 `plugins.bundle.config` 的视图:`summary` 是插件行的一行摘要,两张卡都只属于详情页。 */
|
|
17
|
+
view?: 'summary' | 'page';
|
|
18
|
+
}
|
|
19
|
+
export declare function SettingsCards({ deps, t, view }: SettingsCardsProps): ReactElement | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件设置页两张卡共用的**视图词汇**:卡片外壳配方。
|
|
3
|
+
*
|
|
4
|
+
* 与 `trash-view.ts` 同一个理由:两张卡共用宽度、内距与底色,只有内容节奏(`gap`)不同——各写一份
|
|
5
|
+
* 就会一张改了另一张没改。按内容高、铺满宿主给的宽度,不假设自己住在中央面板里(没有 `height:100%`)。
|
|
6
|
+
*/
|
|
7
|
+
import type { CSSProperties } from 'react';
|
|
8
|
+
/** 设置页卡片外壳:`gap` 是这一张卡自己的内容节奏,其余两张贴一样。 */
|
|
9
|
+
export declare const settingsCardShell: (gap: number) => CSSProperties;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - 标题优先用清册标题,空串才回落到日志的 session/title。
|
|
7
7
|
* - 正文是**块**:用户/助手文字常显;思考、工具、注入、系统消息、压缩摘要默认折叠,折叠头给
|
|
8
8
|
* 标签 + 首行摘要(工具另给状态)。注入也铺进正文——这是对宿主 Chat 的有意偏离(它隐藏普通注入,
|
|
9
|
-
*
|
|
9
|
+
* 而这里的目标是「删之前看全」);条数另进计量行。
|
|
10
10
|
*
|
|
11
11
|
* 助手正文与思考走 Markdown,用户提问保持原话;工具参数/结果走等宽纯文本(限高可滚)。
|
|
12
12
|
* 附件随块位置渲染:用户气泡之上、助手正文之后、工具展开体末尾。
|
|
@@ -1,19 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 回收站界面共用的**视图词汇**:卡片配方、清单行、危险红、项目名与分组的念法。
|
|
3
3
|
*
|
|
4
4
|
* 它从 `trash-panel.tsx` 拆出来,因为清单面板与两个确认框(`trash-confirm.tsx`)都要用这几样——
|
|
5
5
|
* 拆之前它们同住一个文件、只能靠"同一个文件里的常量"共享。拆之后两处读的是同一份,不是两份抄写。
|
|
6
6
|
*
|
|
7
7
|
* **配色与圆角一律走 token,字号是字面量 px**(宿主同类面也这么写):卡片用宿主设置面的底层 token
|
|
8
|
-
* (`bg-layer-2` + `border-l4` + `radius-lg
|
|
8
|
+
* (`bg-layer-2` + `border-l4` + `radius-lg`),清单行不铺底不描边、选中态只换宿主"选中"那一档底色。
|
|
9
9
|
*/
|
|
10
10
|
import type { CSSProperties } from 'react';
|
|
11
11
|
import type { Translate } from './copy.js';
|
|
12
12
|
import type { TrashEntryLike } from './remote.js';
|
|
13
13
|
/** 卡片配方:宿主设置面的 fill/stroke 底层 token + 官方 `lg` 圆角。 */
|
|
14
14
|
export declare const CARD: CSSProperties;
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* 清单条目行:与 `CARD` 同族但**不铺底、不描边**。
|
|
17
|
+
*
|
|
18
|
+
* 为什么和 `CARD` 分成两份而不改 `CARD`:`CARD` 还被读数失败块与附件回执块借用(`trash-panel.tsx`
|
|
19
|
+
* 的 `role="status"`、`attachment-view.tsx`),那一类是"要打断你视线的一块",带底色和边框才对;
|
|
20
|
+
* 清单行是"一列可以扫的东西",边框叠出来就是 2010 年代后台面板的长相。
|
|
21
|
+
*
|
|
22
|
+
* 悬停底色与动作列的低强调/提亮住在 `ui/layout.css`:inline style 表达不了 `:hover` /
|
|
23
|
+
* `:focus-within`,而 `ui.css` 是照宿主明文逐字抄的对照表,混进自造规则会把保真复核弄脏
|
|
24
|
+
* (同 `forms.css` 文件头给的理由)。
|
|
25
|
+
*/
|
|
26
|
+
export declare const ROW: CSSProperties;
|
|
27
|
+
/**
|
|
28
|
+
* 选中行:底色用 inline style **故意压在** `layout.css` 的悬停底色之上——元素上的声明优先于
|
|
29
|
+
* 样式表里的 `:hover` 规则,选中与悬停同时成立时看到的是选中那一档,不会闪。
|
|
30
|
+
*/
|
|
31
|
+
export declare const ROW_SELECTED: CSSProperties;
|
|
17
32
|
/**
|
|
18
33
|
* **整页唯一那块实心红**的写法:`variant="primary"` + 在元素上覆盖 primary 那对填充 token。
|
|
19
34
|
* 宿主插件管理页的 danger 按钮就是这个写法(asar @21658274),所以 hover 仍走 `.primary:hover` 类。
|
|
@@ -33,9 +48,20 @@ export declare function projectLabel(projectDir: string, t: Translate): string;
|
|
|
33
48
|
export interface ProjectGroup {
|
|
34
49
|
projectDir: string;
|
|
35
50
|
label: string;
|
|
51
|
+
/** 组头悬停的全文:未分组组用组名,其余用有损的原始 `projectDir`。 */
|
|
52
|
+
title: string;
|
|
53
|
+
/** 「未分组」组:工作区已不在账本里的条目都归这里,永远沉底。 */
|
|
54
|
+
ungrouped: boolean;
|
|
36
55
|
/** 组内最新一条的删除时间——组间排序的键。 */
|
|
37
56
|
latest: number;
|
|
38
57
|
entries: readonly TrashEntryLike[];
|
|
39
58
|
}
|
|
40
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* 「未分组」组的键:工作区已从宿主账本删掉(`workspaceMissing`)的条目都归这里。
|
|
61
|
+
* 哨兵值不与真实 `projectDir` 同形——空 cwd 已经由 `projectLabel` 念成「未记录项目」,
|
|
62
|
+
* 两者不是同一件事,不能撞键。
|
|
63
|
+
*/
|
|
64
|
+
export declare const UNGROUPED_PROJECT_DIR = "__dsh-session-delete__ungrouped";
|
|
65
|
+
/** 按项目分组:组内按删除时间倒序,组间按各组最新一条倒序(刚删掉的那个项目排在最上面)。
|
|
66
|
+
* 工作区已不存在的条目统一进「未分组」组,且该组永远沉底——它不是一个可比较的项目。 */
|
|
41
67
|
export declare function groupByProject(entries: readonly TrashEntryLike[], t: Translate): ProjectGroup[];
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 本插件 UI
|
|
3
|
-
*
|
|
2
|
+
* 本插件 UI 层要用的图标。除**恢复箭头**外逐条抄自 `@deepseek-ai/dsh-client-ui-primitives`
|
|
3
|
+
* 的明文产物(出处与改写规则见 `ui.css` 头注)。
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* 只留本插件用到的:垃圾桶、警示、文件夹(开盖填充 / 闭盒描边)、**实心三角**(分组头悬停时的
|
|
6
|
+
* 折叠指示)、关闭叉、圆圈对勾、对勾、下折/右折 chevron、恢复(本插件自绘的回退箭头)、
|
|
7
|
+
* 子会话分支(branch)。
|
|
8
|
+
* **官方字形几何、`viewBox`、`strokeWidth` 逐字不动**——改了就不再与宿主同屏同形。
|
|
9
|
+
*
|
|
10
|
+
* **唯一的自绘字形是 `IconRestoreOutlineRegular`**:官方集里 `unarchive-outline`(箱子)与归档动作太像、
|
|
11
|
+
* `refresh-outline`(顺时针圆箭头)读起来是"刷新",都没有"把这条会话放回去"的意思。自绘的 ↩ 沿用宿主
|
|
12
|
+
* 16×16 / 1px 描边的一族几何,不伪装成官方图标。
|
|
7
13
|
*
|
|
8
14
|
* 两条与官方实现的差异(都是减法,不是改写):
|
|
9
15
|
* - 只暴露 `size`:调用处只传 `size`,不摊开官方整个图标 API。
|
|
@@ -16,13 +22,23 @@ export interface IconProps {
|
|
|
16
22
|
}
|
|
17
23
|
export declare function IconTrashOutlineRegular({ size }: IconProps): ReactElement;
|
|
18
24
|
export declare function IconWarningOutlineRegular({ size }: IconProps): ReactElement;
|
|
19
|
-
export declare function IconFolderOpenOutlineRegular({ size }: IconProps): ReactElement;
|
|
20
25
|
export declare function IconCloseOutlineRegular({ size }: IconProps): ReactElement;
|
|
21
26
|
/** 撤销提示 success 档的前导 glyph(官方 `Toast` 在 `tone="success"` 时自带的那一枚)。 */
|
|
22
27
|
export declare function IconCheckCircleOutlineRegular({ size }: IconProps): ReactElement;
|
|
23
28
|
/** 行尾兜底勾选开关那枚对勾(状态点/归档行;官方几何逐字)。 */
|
|
24
29
|
export declare function IconCheckOutlineRegular({ size }: IconProps): ReactElement;
|
|
25
|
-
/**
|
|
30
|
+
/** 分组头:展开时(官方 `IconFolderOpenRegular`,16×16;开盖那一片带 0.16 透明度)。 */
|
|
31
|
+
export declare function IconFolderOpenRegular({ size }: IconProps): ReactElement;
|
|
32
|
+
/** 分组头:收起时(官方 `IconFolderCloseRegular`,16×16)。 */
|
|
33
|
+
export declare function IconFolderCloseRegular({ size }: IconProps): ReactElement;
|
|
34
|
+
/** 分组头悬停时替换文件夹的折叠指示(官方 `IconTriangleRightFillRegular`);展开时旋转 90°。 */
|
|
35
|
+
export declare function IconTriangleRightFillRegular({ size }: IconProps): ReactElement;
|
|
36
|
+
/** 折叠头与标准档「工作步骤」组「展开中」那一枚(官方 `IconChevronDownOutlineRegular`,`viewBox`/`d` 逐字)。 */
|
|
26
37
|
export declare function IconChevronDownOutlineRegular({ size }: IconProps): ReactElement;
|
|
27
|
-
/**
|
|
38
|
+
/** 折叠头与子会话导航行「已折叠 / 前进」那一枚(官方 `IconChevronRightOutlineRegular`)。 */
|
|
28
39
|
export declare function IconChevronRightOutlineRegular({ size }: IconProps): ReactElement;
|
|
40
|
+
/** 行内「恢复」:**本插件自绘**的回退箭头(↩,16×16 / 1px,与宿主图标同一族几何)。
|
|
41
|
+
* 官方集里的 `unarchive` 与归档动作的"箱子"轮廓太近、`refresh` 读作"刷新",都不合适。 */
|
|
42
|
+
export declare function IconRestoreOutlineRegular({ size }: IconProps): ReactElement;
|
|
43
|
+
/** 子会话行的前导:官方 `IconBranchOutlineRegular`(16×16)。 */
|
|
44
|
+
export declare function IconBranchOutlineRegular({ size }: IconProps): ReactElement;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 详情分页的读侧编排:手动「加载更早」、简洁档自动连翻、prepend 后的滚动锚点。
|
|
3
|
+
* 组件只画;分页的两笔账(连翻预算与滚动位置)都住这里。
|
|
4
|
+
*/
|
|
5
|
+
import { type RefObject } from 'react';
|
|
6
|
+
import type { TrashDetailBlockWire } from '../shared/wire.js';
|
|
7
|
+
/**
|
|
8
|
+
* 「加载更早」跟工作步骤档位联动时,一次点击最多自动连翻多少页。
|
|
9
|
+
*
|
|
10
|
+
* 服务端按**块**翻页;简洁档把过程块整段藏起来,一页可能全是过程块 ⇒ 点一下什么都不出现,
|
|
11
|
+
* 看着像没反应。所以简洁档下自动继续往前翻,直到这一批里出现对话块、翻完、或到上限。
|
|
12
|
+
* 切进简洁档时当前页若全是过程块,也按同一份预算自动连翻——否则用户看到的仍是一页空白。
|
|
13
|
+
* 上限防的是"一条几乎全是工具调用的巨型会话"把一次点击变成几十个串行请求。
|
|
14
|
+
*/
|
|
15
|
+
export declare const AUTO_OLDER_PAGE_LIMIT = 10;
|
|
16
|
+
interface DetailPagerOptions {
|
|
17
|
+
blocks: readonly TrashDetailBlockWire[];
|
|
18
|
+
olderCursor: number | null;
|
|
19
|
+
loadingOlder: boolean;
|
|
20
|
+
/** 更早一页读失败的原文;正文本身仍是 ready。 */
|
|
21
|
+
olderFailure: string | null;
|
|
22
|
+
/** 简洁档才自动连翻;标准及以上每页至少有一行可见内容(过程组头)。 */
|
|
23
|
+
compact: boolean;
|
|
24
|
+
onLoadOlder: () => void;
|
|
25
|
+
/** 详情栏的滚动容器;prepend 更早一页后按高度差补偿滚动位置。 */
|
|
26
|
+
scrollRef?: RefObject<HTMLDivElement | null> | undefined;
|
|
27
|
+
}
|
|
28
|
+
export interface DetailPagerHandle {
|
|
29
|
+
/** 手动「加载更早」与切进简洁档时那一次自动翻页共用的入口:记下这一批的起点与预算,再交给上层取数。 */
|
|
30
|
+
loadOlder: () => void;
|
|
31
|
+
}
|
|
32
|
+
export declare function useDetailPager({ blocks, olderCursor, loadingOlder, olderFailure, compact, onLoadOlder, scrollRef, }: DetailPagerOptions): DetailPagerHandle;
|
|
33
|
+
export {};
|
package/lib/types/config.d.ts
CHANGED
|
@@ -3,21 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 为什么不用 `@deepseek-ai/schemastery` 写 schema:那是宿主包,值 import 会破本仓「跨插件一律
|
|
5
5
|
* `import type`」的硬约束;Cordis 的 Config 接受 Standard Schema(`@standard-schema/spec`),
|
|
6
|
-
* 手写这一小段即可,不需要把宿主 schema
|
|
7
|
-
*
|
|
6
|
+
* 手写这一小段即可,不需要把宿主 schema 库搬进插件。代价是宿主只为 native Schemastery schema 投影
|
|
7
|
+
* 字段表单,这枚手写的 Standard Schema 不在其列——字段表单由插件设置页的「可调项」卡提供
|
|
8
|
+
* (ADR-0043),profile 的 `cordis.patch.yml` 仍是默认值与覆盖的家。
|
|
8
9
|
*
|
|
9
10
|
* 这里只做**默认值、归一与 schema**,不持有运行期现场:`apply` 归一一次,结果作为
|
|
10
11
|
* `CoreDeps.config` 注入;读方从自己的 deps 读,不 import 模块级单例。
|
|
11
12
|
* 客户端 6 秒撤销时长不在这里:它是浏览器半的产品决定,不跨半传。
|
|
12
13
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
readonly maxJsonRefBytes: number;
|
|
20
|
-
}
|
|
14
|
+
import { type SessionDeleteConfigWire } from './shared/wire.js';
|
|
15
|
+
/** 三只可调项。字段名是线上契约(`shared/wire.ts` 的 `SessionDeleteConfigWire`),这里只是别名;
|
|
16
|
+
* Node 半读到的是归一后的只读快照,所以 `readonly` 保留。默认值、归一与 Standard Schema 住本文件,
|
|
17
|
+
* 接受范围住在 `shared/wire.ts` 的 `CONFIG_LIMITS`:表单与归一必须是同一格,不然会「表单说能存、
|
|
18
|
+
* 激活时报越界」。 */
|
|
19
|
+
export type SessionDeleteConfig = Readonly<SessionDeleteConfigWire>;
|
|
21
20
|
export declare const DEFAULT_CONFIG: SessionDeleteConfig;
|
|
22
21
|
/**
|
|
23
22
|
* 把 patch 里的 `config` 归一成完整配置:缺省取默认值,未知键 / 非整数 / 越界都抛。
|
|
@@ -33,7 +32,7 @@ export declare const Config: {
|
|
|
33
32
|
readonly version: 1;
|
|
34
33
|
readonly vendor: "dsh-session-delete";
|
|
35
34
|
readonly validate: (value: unknown) => {
|
|
36
|
-
value:
|
|
35
|
+
value: Readonly<SessionDeleteConfigWire>;
|
|
37
36
|
issues?: never;
|
|
38
37
|
} | {
|
|
39
38
|
issues: {
|
package/lib/types/deps.d.ts
CHANGED
|
@@ -23,8 +23,16 @@ export type DeleteSessionDeps = CoreDeps & {
|
|
|
23
23
|
export type RemovalDeps = CoreDeps & {
|
|
24
24
|
fs: CoreFsPort & Pick<FsPort, 'removeDir' | 'removeFile'>;
|
|
25
25
|
};
|
|
26
|
+
/** 宿主 `configEditor` 的窄面:`edit(entry, change)` 把完整 raw config 落盘并让 Loader 重启这一行。
|
|
27
|
+
* `entry` 是 `ctx.fiber.entry`(Loader 挂在 fiber 上,Cordis 类型不声明它,组合根按窄镜像取)。 */
|
|
28
|
+
export interface ConfigEditorLike {
|
|
29
|
+
edit(entry: unknown, change: (current: Record<string, unknown>) => Record<string, unknown>): Promise<void>;
|
|
30
|
+
}
|
|
26
31
|
/** 组合根装出的全量面:`/apply` 的分发同时够到删除的时钟与永久删除的 `removeDir` / `removeFile`
|
|
27
|
-
* (后者也供孤儿附件清理段使用)。`attachments` 只给 `attachment` 读段(ADR-0035);动词不拿它。
|
|
32
|
+
* (后者也供孤儿附件清理段使用)。`attachments` 只给 `attachment` 读段(ADR-0035);动词不拿它。
|
|
33
|
+
* `configEditor` / `entry` 只给 `/config` 的写支;宿主没有这套面时写支回 `io`,读支照常。 */
|
|
28
34
|
export type DeleteDeps = DeleteSessionDeps & RemovalDeps & {
|
|
29
35
|
readonly attachments: AttachmentsPort;
|
|
36
|
+
readonly configEditor?: ConfigEditorLike;
|
|
37
|
+
readonly entry?: unknown;
|
|
30
38
|
};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** 只用于类型面:`@deepseek-ai/cordis` 留在 devDependencies 供本仓与 `lib/types` 解析,
|
|
2
|
+
* 不是 peer、也不在运行期加载;纯类型消费者在宿主外使用本包时需要自备它。 */
|
|
1
3
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
4
|
import { Config } from './config.js';
|
|
3
5
|
export declare const name = "@xrn1997/dsh-session-delete";
|
|
@@ -18,7 +20,7 @@ export { Config };
|
|
|
18
20
|
* - `agents`:只给 handle 捕获用(`ctx.get('agents')`;缺席时 `ctx.inject(['agents'])` 等服务就绪再装)。
|
|
19
21
|
* 它同样可能晚于本插件激活,写进插件 inject 只会让整个插件 pending。
|
|
20
22
|
*
|
|
21
|
-
* `connection`
|
|
23
|
+
* `connection` 是浏览器半取数通道的唯一承载者:十段注册到 `connection.fetch.register` 的
|
|
22
24
|
* `/api/dsh-session-delete-api/*`,Host/Origin/浏览器认证由宿主 `/api` bridge 完成(ADR-0033)。
|
|
23
25
|
* 支持面是 web UI 与桌面端,二者的标准组合都装 `client-connection`;服务缺席时插件停在 pending
|
|
24
26
|
* ——可见的激活失败,而不是静默地没有入口。`webServer` 不再直接使用:exact route 仍由宿主的
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
/** 捕获所需的 ctx 窄面:可选服务读法(`get
|
|
2
|
-
*
|
|
1
|
+
/** 捕获所需的 ctx 窄面:可选服务读法(`get`)、按依赖注入的子上下文(`inject`,晚挂载的 `agents` 靠它补装)
|
|
2
|
+
* 与卸载钩子(`effect`,wrapper 的恢复挂在这里)。有些 ctx / 假件没有后两者——那就只在 apply 时 `get` 一次,
|
|
3
|
+
* 且不注册恢复。 */
|
|
3
4
|
export interface AgentCtxLike {
|
|
4
5
|
get(name: string): unknown;
|
|
5
6
|
inject?(names: readonly string[], callback: (child: {
|
|
6
7
|
get(name: string): unknown;
|
|
7
8
|
}) => void): void;
|
|
9
|
+
effect?(execute: () => () => unknown, label?: string): unknown;
|
|
8
10
|
}
|
|
9
11
|
/** 一次捕获交回的句柄表:按会话 id `dispose`。没捕到 / 已经 dispose 过都是 no-op;
|
|
10
12
|
* dispose 本身的失败**原样抛出**(调用方 `evictLive` 把它折成 `unknown`,不通告)。 */
|
|
@@ -19,6 +21,12 @@ export interface AgentHandleStore {
|
|
|
19
21
|
* **安装时机**:先 `get('agents')`,没装成再 `inject(['agents'])`(两者只装一次,符号表兜住)。
|
|
20
22
|
* **symbol 表**:句柄表挂在服务实例的 {@link CAPTURED_AGENT_HANDLES} 上;同一个 agents 服务上的第二次
|
|
21
23
|
* 安装(`get` 与 `inject` 各一次,或第二个 `createHostPort`)看见它就直接共用,不把 wrapper 套第二层。
|
|
24
|
+
* **安装者拥有 wrapper**:包装记录先在闭包里留一份(`ownWrappers`,建立于 symbol 写入之前),同时挂到
|
|
25
|
+
* {@link CAPTURED_AGENT_WRAPPERS};第一次成功安装时经 `ctx.effect` 注册一条卸载恢复——按 own property
|
|
26
|
+
* descriptor 的身份把 `resume` / `create` 写回目标自有的原函数(`restoreTo`;代理读回的影子不写回,
|
|
27
|
+
* 别人后装的 wrapper 不动),并摘掉仍归本闭包的 symbol 表。恢复是总的:一条写回失败不跳过其余记录。
|
|
28
|
+
* 恢复一次即锁死:晚到的安装与晚到的 handle 落地都被 `restored` 挡住。没有 `ctx.effect`(假件 / 旧 ctx)
|
|
29
|
+
* 时包装照装,只是没有恢复。
|
|
22
30
|
* **读回判据**:cordis 的 traceable 服务代理每次读函数都会新包一层(`get` 里的 `createShadowMethod`),
|
|
23
31
|
* 所以只能看 own property descriptor 的身份;拿 `service[name]` 读回比身份是必然的假警报。
|
|
24
32
|
*/
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 跨半 wire
|
|
2
|
+
* 跨半 wire 契约:前缀、十个端点、请求/响应形状与失败码。
|
|
3
3
|
* Node 半(src/api/dispatch.ts)与浏览器半(src/client/remote.ts)共用这一份,不各写字面量。
|
|
4
4
|
*
|
|
5
5
|
* 构建约束:本文件零运行时依赖、无 import;浏览器半把整个文件 inline 进 bundle。
|
|
6
6
|
* 信封 {ok:true,value} / {ok:false,error:{code,message,details?}} 也是两半的共同契约。
|
|
7
7
|
*/
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* 插件专属前缀(十段相对它命名)。
|
|
10
10
|
*
|
|
11
11
|
* 为什么必须带插件名:宿主 `connection.fetch.register` 对**重复的精确路径**直接抛错
|
|
12
12
|
* (`connection: exact Fetch route "…" is already registered`),撞了就是整棵插件树 boot 失败——
|
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
*/
|
|
15
15
|
export declare const SESSION_DELETE_API_PREFIX = "/dsh-session-delete-api";
|
|
16
16
|
/**
|
|
17
|
-
* 唯一的取数基础路径:宿主 `/api` +
|
|
17
|
+
* 唯一的取数基础路径:宿主 `/api` + 插件前缀。十段 exact Fetch 路由、客户端探测与附件 URL
|
|
18
18
|
* 都从它起;`SESSION_DELETE_API_PREFIX` 只作为段名的相对前缀与错误原文。
|
|
19
19
|
*/
|
|
20
20
|
export declare const OFFICIAL_SESSION_DELETE_API_PREFIX = "/api/dsh-session-delete-api";
|
|
21
|
-
/**
|
|
21
|
+
/** 十段段名(相对前缀)。**每项 `{ path, method }` 两键**:某段用什么方法也是线上形状的一部分
|
|
22
22
|
* (ADR-0003),家就在这一张表——路由记录(`src/api/route-table.ts`)逐行投影(`ROUTES.x.method`),
|
|
23
23
|
* 浏览器半的 GET 段同样投影、写段把 `ROUTES.x.method` 交给 `src/client/remote.ts` 的 `post()`,
|
|
24
24
|
* 两半都不逐段手写一份。
|
|
@@ -33,7 +33,8 @@ export declare const OFFICIAL_SESSION_DELETE_API_PREFIX = "/api/dsh-session-dele
|
|
|
33
33
|
* 这一段只负责"有多少、在哪、多大"。
|
|
34
34
|
* - `attachment`:附件原始字节(全仓唯一的二进制响应段;失败仍回 JSON 信封)。
|
|
35
35
|
* - `attachmentOrphans`:无引用图片对象的扫描报告(纯读;数量/字节 + 供预览的对象清单)。
|
|
36
|
-
* - `attachmentClean`:清掉报告那一刻无引用的图片对象(聚合回执 `AttachmentCleanupWire`)。
|
|
36
|
+
* - `attachmentClean`:清掉报告那一刻无引用的图片对象(聚合回执 `AttachmentCleanupWire`)。
|
|
37
|
+
* - `config`:三只可调项的读/写共用一段——空体读当前值,带字段写回 profile 的 patch 层。 */
|
|
37
38
|
export declare const ROUTES: {
|
|
38
39
|
readonly list: {
|
|
39
40
|
readonly path: "/list";
|
|
@@ -44,6 +45,11 @@ export declare const ROUTES: {
|
|
|
44
45
|
readonly path: "/ping";
|
|
45
46
|
readonly method: "GET";
|
|
46
47
|
};
|
|
48
|
+
/** 可调项:空体读(与 `/detail` 同法,用 POST 表达一个读);带字段写,写必须进到达序链。 */
|
|
49
|
+
readonly config: {
|
|
50
|
+
readonly path: "/config";
|
|
51
|
+
readonly method: "POST";
|
|
52
|
+
};
|
|
47
53
|
/** 单条详情(读回收站目录里那条会话自己的日志)。 */
|
|
48
54
|
readonly detail: {
|
|
49
55
|
readonly path: "/detail";
|
|
@@ -83,6 +89,32 @@ export declare const ROUTES: {
|
|
|
83
89
|
/** 段名 → 那一段的路径。从结构化段名表派生:加一段只在上面加一项,`Record<RouteName, …>` 类型闸
|
|
84
90
|
* (`src/api/route-table.ts`)与运行期键集比对(`tests/route-table.test.ts`)都跟着这一格走。 */
|
|
85
91
|
export type RouteName = (typeof ROUTES)[keyof typeof ROUTES]['path'];
|
|
92
|
+
/** 三只可调项的线上形状。Node 半的 `SessionDeleteConfig`(`src/config.ts`)是它的别名:
|
|
93
|
+
* 这一格固定线上字段名与接受范围({@link CONFIG_LIMITS}),默认值与归一在 `src/config.ts`。 */
|
|
94
|
+
export interface SessionDeleteConfigWire {
|
|
95
|
+
/** 设置页「孤儿附件」预览清单最多列多少条(数量与字节仍按全量报告)。 */
|
|
96
|
+
orphanPreviewLimit: number;
|
|
97
|
+
/** `GET /attachment` 单次响应字节上限(服务路径与直接 fs fallback 共用)。 */
|
|
98
|
+
maxAttachmentBytes: number;
|
|
99
|
+
/** 附件引用扫描里单个 JSON 引用源的字节上限;超过就按「读不出」fail closed。 */
|
|
100
|
+
maxJsonRefBytes: number;
|
|
101
|
+
}
|
|
102
|
+
/** 三只可调项的接受范围(闭区间整数)。Node 半的归一与浏览器半的表单共用这一格:
|
|
103
|
+
* 两处各写一份范围就会「表单说能存、宿主激活时说越界」。 */
|
|
104
|
+
export declare const CONFIG_LIMITS: {
|
|
105
|
+
readonly orphanPreviewLimit: {
|
|
106
|
+
readonly min: 1;
|
|
107
|
+
readonly max: 10000;
|
|
108
|
+
};
|
|
109
|
+
readonly maxAttachmentBytes: {
|
|
110
|
+
readonly min: 1;
|
|
111
|
+
readonly max: number;
|
|
112
|
+
};
|
|
113
|
+
readonly maxJsonRefBytes: {
|
|
114
|
+
readonly min: 1;
|
|
115
|
+
readonly max: number;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
86
118
|
/** `/apply` 的五个动作。id 的含义随动作走:`delete`/`count`/`orphan-clean` 是**宿主会话 id**
|
|
87
119
|
* (前两个还没进回收站,第三个压根没有回收站行),`restore`/`purge` 是**回收站条目 id**。
|
|
88
120
|
* 两域都是 string(线上形状);这里不引入品牌类型——错域不靠类型拦,交给各动词在运行期按自己的
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/config` 的读/写动词:三只可调项的当前值与写回。
|
|
3
|
+
*
|
|
4
|
+
* 读是免费快照(`apply` 归一后的那一份),写才碰宿主:
|
|
5
|
+
* - 先按 `src/config.ts` 的 `normalizeConfig` 校验(未知键 / 非整数 / 越界回 400,不拿半套值写盘);
|
|
6
|
+
* - 再走宿主 `configEditor.edit(entry, change)`:完整 raw config 落进 profile 的 patch 层,
|
|
7
|
+
* 由编辑器串行化、校验并让这一行重启(字段不是 volatile,重启是它给的语义)。
|
|
8
|
+
* - `configEditor` / `entry` 缺席(老宿主、嵌套上下文)回 `io` 并在原文里说清替代路径——
|
|
9
|
+
* 不静默假装保存成功。
|
|
10
|
+
*
|
|
11
|
+
* `entry` 由组合根从 `ctx.fiber.entry` 取(见 `src/index.ts`);本文件不碰 ctx。
|
|
12
|
+
*/
|
|
13
|
+
import { type SessionDeleteConfig } from '../config.js';
|
|
14
|
+
import type { ConfigEditorLike } from '../deps.js';
|
|
15
|
+
/** `/config` 的写支需要的三件;读支只要 `config`。 */
|
|
16
|
+
export interface ConfigRouteDeps {
|
|
17
|
+
readonly config: SessionDeleteConfig;
|
|
18
|
+
readonly configEditor?: ConfigEditorLike;
|
|
19
|
+
readonly entry?: unknown;
|
|
20
|
+
}
|
|
21
|
+
/** `/config` 的请求体:`null` 或 `{}` 是读;只要带一个键就当写(未知键由 {@link normalizeConfig} 400 拒掉)。
|
|
22
|
+
* 路由表按同一句话定纯度,所以别在别处再写一遍这个判据。 */
|
|
23
|
+
export declare function configPatchOf(body: Record<string, unknown> | null): Record<string, unknown> | undefined;
|
|
24
|
+
/** 空体 = 读当前值;带字段 = 写。请求体已经由适配器解析成对象或 null。 */
|
|
25
|
+
export declare function readOrSaveConfig(deps: ConfigRouteDeps, body: Record<string, unknown> | null): SessionDeleteConfig | Promise<SessionDeleteConfig>;
|
package/locale/en.json
CHANGED
package/locale/zh.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
2
|
-
"meta": {
|
|
3
|
-
"title": "会话删除",
|
|
4
|
-
"description": "删除会话:删除 = 移入回收站(可恢复),另设显式的彻底删除。"
|
|
5
|
-
}
|
|
6
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "会话删除",
|
|
4
|
+
"description": "删除会话:删除 = 移入回收站(可恢复),另设显式的彻底删除。"
|
|
5
|
+
}
|
|
6
|
+
}
|