dsh-session-ops 0.0.0-stage → 0.1.4

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cooper Zhuang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,118 @@
1
- # Temporary Holding Version
1
+ # dsh-session-ops
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Session operations for DeepSeek Harness (DSH): **really delete** persisted sessions and **move**
4
+ sessions between workspaces while keeping the workspace registry consistent.
5
+
6
+ One package, one host-plugin row, two halves:
7
+
8
+ | Half | What it does | Entry points |
9
+ | --- | --- | --- |
10
+ | delete | filter by workspace / archive / subagent, batch select, per-session guards; removes data from disk | HTTP `/session-admin/api/*`, model tool `session_admin`, right-pane "会话管理" tab, conversation-header trash icon, settings page "会话工具" |
11
+ | move | relocate the log, rewrite `header.cwd`, follow workspace membership; optional memory follow (off by default) | HTTP `/session-move/api/*`, model tool `session_move`, right-pane "会话归位" tab, session context menu |
12
+
13
+ The session-storage primitives live in this package under `lib/store/` (project-directory key
14
+ codec, `session[.vN].jsonl[.zstd]` frame read/write, workspace registry, `$DSH_HOME` and file
15
+ logging, HTTP plumbing) and are shared by both halves.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ dsh plugin --profile <profile> add dsh-session-ops
21
+ ```
22
+
23
+ Restart the host afterwards (or let DSH recompose the profile according to its hot-load rules).
24
+ The plugin row is declared in the bundled `cordis.patch.yml`; its entry id is `session-toolbox`.
25
+
26
+ ## ⚠️ Destructive deletion
27
+
28
+ "Delete" means **really delete**, not archive:
29
+
30
+ - `<DSH_HOME>/sessions/<project-key>/<session-id>/` (session log)
31
+ - `<DSH_HOME>/storages/session_projcache/sessions/<session-id>.json` (projection cache)
32
+ - `<DSH_HOME>/rewind-snapshots/<session-id>/` (rewind snapshots, if present)
33
+
34
+ All three are removed from disk, and project directories left empty are cleaned up. **There is no
35
+ trash and no undo.** Back up `$DSH_HOME` yourself; the panel provides a dry run and two
36
+ confirmations, but the operator owns the outcome.
37
+
38
+ Safe defaults (configurable): `allowWorkspaceSessions=false`, `allowLiveSessions=false`,
39
+ `refuseRunning=true` — workspace sessions, live sessions and mid-turn moves are refused by default.
40
+
41
+ A move is not a delete: the session directory is relocated from the old project directory to the
42
+ new one, `header.cwd` is rewritten, and workspace membership follows. On kernel 0.2.2+
43
+ `session.lock` moves with it (same inode); if the target already holds a lease the move is refused
44
+ outright rather than leaving a split lease.
45
+
46
+ ## Configuration
47
+
48
+ The plugin row has one shared `enabled` master switch; every other key is independent:
49
+
50
+ | Key | Default | Meaning |
51
+ | --- | --- | --- |
52
+ | `enabled` | `true` | master switch; `false` registers no routes, tools or slots |
53
+ | `root` | `''` | session root override; empty derives it from `$DSH_HOME/sessions` |
54
+ | `adminAllowHosts` | `[]` | delete-half LAN trust list (parsed from the `Host` header) |
55
+ | `moveAllowHosts` | `[]` | move-half LAN trust list (exact socket `remoteAddress` match) |
56
+ | `headerDeleteButton` | `true` | conversation-header trash icon (icon + two confirmations) |
57
+ | `allowWorkspaceSessions` | `false` | allow deleting sessions that belong to a workspace |
58
+ | `allowLiveSessions` | `false` | allow deleting sessions still loaded in the host |
59
+ | `maxBatch` | `200` | per-call batch delete limit |
60
+ | `refuseRunning` | `true` | refuse moving a session that is mid-turn |
61
+ | `maxSessions` | `2000` | max sessions listed by the move panel |
62
+ | `memoryFollow` | `off` | project-memory follow: `off` / `copy` / `move` (`move` is irreversible) |
63
+ | `repairForkChildrenOnBoot` | `true` | re-file fork children whose membership was lost, at boot |
64
+
65
+ The two trust keys **cannot** be merged: the delete half parses the `Host` header and additionally
66
+ rejects `sec-fetch-site: cross-site` and cross-origin `Origin`, while the move half matches the
67
+ socket address exactly. Merging them would loosen one half. Both only affect LAN access; loopback is
68
+ always trusted.
69
+
70
+ The browser half registers one config card on the Plugins page: slot `plugins.bundle.config`, key =
71
+ the package name `dsh-session-ops`, carrying the seven volatile knobs (4 delete + 3 move). Saving
72
+ writes back to the profile's `cordis.patch.yml`; no restart needed.
73
+
74
+ ## Compatibility
75
+
76
+ - Node.js `>=24`
77
+ - DSH `^0.2.1-rc.2 || ^0.2.2-alpha.2`
78
+
79
+ The session context menu needs the kernel's `@deepseek-ai/dsh-client-ui-context-menu` service
80
+ (0.2.2+); without it only those two menu entries are missing — tabs, tools and HTTP endpoints keep
81
+ working. Moving `session.lock` and the boot-time fork-child repair both follow the 0.2.2+
82
+ persistence implementation.
83
+
84
+ ## Troubleshooting artifacts
85
+
86
+ Two file logs keep their original line formats (historical artifacts, deliberately not unified):
87
+
88
+ - `$DSH_HOME/logs/dsh-session-admin.log` — `[iso] level message`
89
+ - `$DSH_HOME/logs/dsh-session-move.log` — `iso [level] message`
90
+
91
+ Key move/repair lines: `membership repair mounted`, `membership: attached fork child …`,
92
+ `membership: attach failed …`, `membership sweep: re-attached N of M fork child(ren) …`.
93
+
94
+ ## Using it as a library
95
+
96
+ `lib/store/` is independent of the plugin lifecycle and has zero `@deepseek-ai/*` dependencies, so it
97
+ can be imported on its own. Two entries are published:
98
+
99
+ ```js
100
+ // package root: same surface as the former shared session-store entry
101
+ import { projectKey, readSessionHeader, hasZstd } from 'dsh-session-ops'
102
+
103
+ // or the explicit layered subpath
104
+ import { projectKey, readSessionHeader, hasZstd } from 'dsh-session-ops/store'
105
+ ```
106
+
107
+ ## Development
108
+
109
+ ```bash
110
+ npm test # node --test: all use cases from both halves plus lib/store
111
+ ```
112
+
113
+ `lib/` is hand-written source, not build output; `lib/client.js` is an IIFE for the browser classic
114
+ script loader and can be validated with `node --check`.
115
+
116
+ ## License
117
+
118
+ MIT © 2026 Cooper Zhuang
package/README.zh.md ADDED
@@ -0,0 +1,111 @@
1
+ # dsh-session-ops
2
+
3
+ DeepSeek Harness(DSH)的**会话运维插件**:真删除会话、把会话搬到另一个工作区,并把工作区成员关系修好。
4
+
5
+ 一个包、一个插件行,包含两个半边:
6
+
7
+ | 半边 | 能力 | 入口 |
8
+ | --- | --- | --- |
9
+ | 删除 | 按工作区 / 归档 / 子代理筛选、批量选择、逐条守卫;真删磁盘数据 | HTTP `/session-admin/api/*`、模型工具 `session_admin`、右栏「会话管理」页签、会话标题栏垃圾桶、设置页「会话工具」 |
10
+ | 搬迁 | 搬日志、改写 header 的 `cwd`、跟随工作区成员表;可选项目记忆跟随(默认关) | HTTP `/session-move/api/*`、模型工具 `session_move`、右栏「会话归位」页签、会话右键菜单 |
11
+
12
+ 会话存储原语(项目目录键编解码、`session[.vN].jsonl[.zstd]` 帧读写、工作区注册表、
13
+ `$DSH_HOME` 与文件日志、HTTP 底座)收在本包的 `lib/store/`,两个半边共用同一份实现。
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ dsh plugin --profile <profile> add dsh-session-ops
19
+ ```
20
+
21
+ 装好后重启宿主(或按 DSH 的热加载规则让 profile 重组)。插件行由包内
22
+ `cordis.patch.yml` 声明,entry id 是 `session-toolbox`。
23
+
24
+ ## ⚠️ 破坏性删除
25
+
26
+ 「删除」是**真删除**,不是归档:
27
+
28
+ - `<DSH_HOME>/sessions/<项目目录键>/<session-id>/`(会话日志)
29
+ - `<DSH_HOME>/storages/session_projcache/sessions/<session-id>.json`(投影缓存)
30
+ - `<DSH_HOME>/rewind-snapshots/<session-id>/`(回退快照,若有)
31
+
32
+ 三处一起从磁盘移除,并顺手清理因此变空的项目目录。**没有回收站,删了无法恢复。**
33
+ 删除前请自行备份 `$DSH_HOME`;面板提供预演与二次确认,但最终责任在操作者。
34
+
35
+ 默认安全边界(可在配置里改):`allowWorkspaceSessions=false`、`allowLiveSessions=false`、
36
+ `refuseRunning=true` —— 工作区会话、正在跑的会话、运行中的搬迁默认都被拒绝。
37
+
38
+ 搬迁不是删除:日志目录从旧 project 目录整体移到新目录,`header.cwd` 改写,工作区
39
+ 成员表跟着变;0.2.2+ 内核里 `session.lock` 会一起搬走(inode 不变),目标目录已有
40
+ 租约时直接拒绝,不会制造「锁在旧目录、日志在新目录」的半套状态。
41
+
42
+ ## 配置
43
+
44
+ 插件行只有一个 `enabled` 总开关,两半共用;其余键各自独立:
45
+
46
+ | 键 | 默认 | 作用 |
47
+ | --- | --- | --- |
48
+ | `enabled` | `true` | 总开关;`false` 时不注册任何路由、工具、槽位 |
49
+ | `root` | `''` | 会话根目录;空表示用 `$DSH_HOME/sessions` |
50
+ | `adminAllowHosts` | `[]` | 删除半边的 LAN 信任列表(按 `Host` 头解析 host/hostname 匹配) |
51
+ | `moveAllowHosts` | `[]` | 搬迁半边的 LAN 信任列表(按 socket `remoteAddress` 精确匹配) |
52
+ | `headerDeleteButton` | `true` | 会话标题栏垃圾桶图标(图标 + 两步确认) |
53
+ | `allowWorkspaceSessions` | `false` | 允许删除已归属工作区的会话 |
54
+ | `allowLiveSessions` | `false` | 允许删除宿主里驻留的会话 |
55
+ | `maxBatch` | `200` | 单次批量删除上限 |
56
+ | `refuseRunning` | `true` | 拒绝搬迁正在跑一轮的会话 |
57
+ | `maxSessions` | `2000` | 搬迁面板一次最多列出的会话数 |
58
+ | `memoryFollow` | `off` | 项目记忆跟随:`off` / `copy` / `move`(`move` 不可逆) |
59
+ | `repairForkChildrenOnBoot` | `true` | 启动时补挂漏记账的 fork 子会话 |
60
+
61
+ 两个信任键**不能合并**:删除半边按 `Host` 头并拒绝 `sec-fetch-site: cross-site` 与
62
+ 跨源 `Origin`,搬迁半边按 socket 地址精确匹配;合成一个等于放宽其中一边。两者都只
63
+ 影响 LAN 访问,loopback 始终被信任。
64
+
65
+ 浏览器侧还有一处指向本行的配置卡片:槽 `plugins.bundle.config`,key 是包名
66
+ `dsh-session-ops`,7 个 volatile 旋钮(删除 4 + 搬迁 3)保存即写回 profile 的
67
+ `cordis.patch.yml`,无需重启。
68
+
69
+ ## 兼容区间
70
+
71
+ - Node.js `>=24`
72
+ - DSH `^0.2.1-rc.2 || ^0.2.2-alpha.2`
73
+
74
+ 会话右键菜单需要内核提供 `@deepseek-ai/dsh-client-ui-context-menu`(0.2.2+);没有这个
75
+ 服务时只少两个菜单项,页签、工具与 HTTP 入口照旧。搬迁时携带 `session.lock`、
76
+ 启动期 fork 子会话补记账同样以 0.2.2+ 的持久化实现为准。
77
+
78
+ ## 排障产物
79
+
80
+ 两份文件日志保留各自原有的行格式(历史产物,刻意不统一):
81
+
82
+ - `$DSH_HOME/logs/dsh-session-admin.log` —— `[时间] 级别 正文`
83
+ - `$DSH_HOME/logs/dsh-session-move.log` —— `时间 [级别] 正文`
84
+
85
+ 搬迁与自愈的关键行:`membership repair mounted`、`membership: attached fork child …`、
86
+ `membership: attach failed …`、`membership sweep: re-attached N of M fork child(ren) …`。
87
+
88
+ ## 作为库使用
89
+
90
+ `lib/store/` 与插件生命周期无关、零 `@deepseek-ai/*` 依赖,可以单独引用。包里提供两种入口:
91
+
92
+ ```js
93
+ // 根入口(与合并前的会话存储共享库公开面一致)
94
+ import { projectKey, readSessionHeader, hasZstd } from 'dsh-session-ops'
95
+
96
+ // 或显式分层,避免和插件面混在一起
97
+ import { projectKey, readSessionHeader, hasZstd } from 'dsh-session-ops/store'
98
+ ```
99
+
100
+ ## 开发
101
+
102
+ ```bash
103
+ npm test # node --test:两个半边全部用例 + lib/store 用例
104
+ ```
105
+
106
+ `lib/` 是手写源码,不走构建;`lib/client.js` 是给浏览器经典脚本加载器用的 IIFE,
107
+ 直接用 `node --check` 即可校验。
108
+
109
+ ## License
110
+
111
+ MIT © 2026 Cooper Zhuang
@@ -0,0 +1,38 @@
1
+ # dsh-session-ops bundle patch: mount the single host-plane plugin row.
2
+ #
3
+ # 合并包:一个插件行同时提供原先两个插件的能力。
4
+ # - 删除(原 dsh-session-admin):HTTP /session-admin/api,模型工具 session_admin,
5
+ # 右栏「会话管理」页签,会话标题栏垃圾桶图标,设置页「会话清理」卡片。
6
+ # - 搬迁(原 dsh-session-move):HTTP /session-move/api,模型工具 session_move,
7
+ # 右栏「会话归位」页签(含左栏底部入口)。
8
+ # 两个半边共用本行的这一份配置;插件页上本行的「配置」卡片就是合并后的那张(7 个旋钮)。
9
+ #
10
+ # 从旧的两个插件行迁过来时,配置键要改写(旧 allowHosts 语义不同,已拆成两个键):
11
+ # dsh-session-admin 行:enabled→enabled, allowHosts→adminAllowHosts,
12
+ # headerDeleteButton/allowWorkspaceSessions/allowLiveSessions/maxBatch 同名保留。
13
+ # dsh-session-move 行:enabled→enabled, allowHosts→moveAllowHosts,
14
+ # root/refuseRunning/maxSessions/memoryFollow 同名保留。
15
+ # 注意:enabled 从「每半一个」变成一个(总开关)。旧 profile 里残留的 allowHosts
16
+ # 不会被任何栅栏读取(schemastery 容忍未知键,不会报错),也就是会**静默失效**——
17
+ # 需要 LAN 访问的话,必须把它改写成 adminAllowHosts / moveAllowHosts。
18
+ #
19
+ # 安全默认(一字未变):
20
+ # admin/headerDeleteButton=true(垃圾桶是图标 + 两步确认,不是带标签的按钮)
21
+ # admin/allowWorkspaceSessions=false, admin/allowLiveSessions=false, admin/maxBatch=200
22
+ # move/refuseRunning=true, move/maxSessions=2000, move/memoryFollow=off
23
+ - insert:
24
+ - id: session-toolbox
25
+ name: dsh-session-ops
26
+ config:
27
+ enabled: true
28
+ root: ''
29
+ adminAllowHosts: []
30
+ moveAllowHosts: []
31
+ headerDeleteButton: true
32
+ allowWorkspaceSessions: false
33
+ allowLiveSessions: false
34
+ maxBatch: 200
35
+ refuseRunning: true
36
+ maxSessions: 2000
37
+ # off(默认)| copy | move —— 见本包 README「配置」一节的 memoryFollow。
38
+ memoryFollow: off
package/docs/DETAIL.md ADDED
@@ -0,0 +1,24 @@
1
+ # Session operations
2
+
3
+ Turns the session-to-workspace binding into an explicit tool: really delete sessions and move them
4
+ between workspaces while keeping the registry consistent.
5
+
6
+ ## Two right-pane tabs
7
+ - Delete: filter by workspace, archive or subagent; batch select; running sessions refused by default.
8
+ - Move: relocate the log, rewrite header cwd, follow workspace membership; optional memory follow (off).
9
+
10
+ ## Other entry points
11
+ - Session context menu: move / delete.
12
+ - Model tools session_admin, session_move; host HTTP /session-admin/api, /session-move/api.
13
+
14
+ ## Shared storage primitives (`lib/store`)
15
+ - `paths.js`: project-directory key codec (`projectKey` / `encodeSegment`), session and generation file naming.
16
+ - `frames.js`: `session[.vN].jsonl[.zstd]` frame scanning, header read (two strictness levels), cwd rewrite.
17
+ - `registry.js`: workspace membership and archive sets, resident sessions, mid-turn detection, write-handle redirection.
18
+ - `host.js`: `$DSH_HOME`, best-effort file logging, volatile config unwrapping.
19
+ - `http.js`: shared JSON response / request-body plumbing and the prefixed route skeleton.
20
+
21
+ ## Notes
22
+ - A move carries session.lock (0.2.2+ write lease) and empties the source directory; it refuses when the target already holds a lease.
23
+ - On boot a narrow fork-child membership repair runs (repairForkChildrenOnBoot, default on).
24
+ - Deletion is permanent: no trash, no undo. Back up `$DSH_HOME` before using it.
@@ -0,0 +1,23 @@
1
+ # 会话运维
2
+
3
+ 把 DSH 的「会话 → 工作区」绑定关系做成可操作的工具:真实删会话、把会话搬到另一个工作区,并把成员关系修好。
4
+
5
+ ## 两个页签
6
+ - 删除:按工作区、归档、子代理筛选,批量选择;运行中会话默认拒绝。
7
+ - 搬迁:列出会话与归属,选择目标工作区后搬日志、改 header cwd、跟随注册表成员;支持记忆条目跟随(默认关)。
8
+
9
+ ## 其他入口
10
+ - 会话右键菜单:移动会话到工作区…、删除会话…
11
+ - 模型工具 session_admin、session_move;宿主 HTTP /session-admin/api、/session-move/api。
12
+
13
+ ## 共享存储原语(`lib/store`)
14
+ - `paths.js`:项目目录 key 编解码(projectKey / encodeSegment)、会话目录与 generation 文件名规则。
15
+ - `frames.js`:`session[.vN].jsonl[.zstd]` 帧扫描、header 读取(两种严格度)、cwd 改写。
16
+ - `registry.js`:工作区成员表 / 归档集合、驻留会话、「正在跑一轮」判定、写入句柄重定向。
17
+ - `host.js`:`$DSH_HOME`、尽力而为的文件日志、volatile 配置解包。
18
+ - `http.js`:JSON 响应 / 请求体读取底座与前缀路由骨架。
19
+
20
+ ## 注意
21
+ - 搬迁会把 session.lock 一起搬走(0.2.2+ 写租约),源目录清空;目标已有租约时拒绝。
22
+ - 启动时会做一次 fork 子会话的定向补记账(repairForkChildrenOnBoot,默认开)。
23
+ - 删除是永久性的:没有回收站、无法恢复,操作前请备份 `$DSH_HOME`。
package/icon.svg ADDED
@@ -0,0 +1 @@
1
+ <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="6" y="12" width="24" height="16" rx="3" fill="#F59E0B"/><path d="M6 12l3-5h6l2 5M21 12l2 5h7" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>