dsh-turn-outline 0.2.5 → 0.2.7
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.en.md +17 -9
- package/README.md +17 -9
- package/lib/client.js +42 -25
- package/lib/client.js.map +2 -2
- package/lib/index.js +29 -13
- package/lib/index.js.map +2 -2
- package/package.json +11 -14
package/README.en.md
CHANGED
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
[中文](README.md)
|
|
11
11
|
|
|
12
|
+
## Preview
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<img src="docs/assets/turn-outline-panel.png" alt="Turn Outline tab: the session folded into per-user turns of input, tool steps and output" width="420" />
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
> Real-session screenshot: the whole session folds into turns 1-5, each = user input + tool steps + final output; the tool filter chips at the top (All / bash / read / edit / More) aggregate by semantic dot, and clicking any row jumps back to its place in the conversation.
|
|
19
|
+
|
|
12
20
|
## Overview
|
|
13
21
|
|
|
14
22
|
**Who it is for**: users of DeepSeek Harness (Web / Desktop profile) with long sessions who want to quickly revisit "what happened at step N" — at zero cost, without adding an LLM or touching the DSH core.
|
|
@@ -29,24 +37,24 @@
|
|
|
29
37
|
|
|
30
38
|
### Option 1: one-liner from npm (recommended)
|
|
31
39
|
|
|
32
|
-
Install better-sidebar first
|
|
40
|
+
Install better-sidebar first, then this plugin:
|
|
33
41
|
|
|
34
42
|
```sh
|
|
35
|
-
npx @deepseek-ai/dsh plugin --profile desktop add dsh-better-sidebar
|
|
43
|
+
npx @deepseek-ai/dsh plugin --profile desktop add dsh-better-sidebar
|
|
36
44
|
npx @deepseek-ai/dsh plugin --profile desktop add dsh-turn-outline
|
|
37
45
|
```
|
|
38
46
|
|
|
39
47
|
> Using the Web profile? Swap `--profile desktop` for `--profile web`.
|
|
40
48
|
>
|
|
41
|
-
>
|
|
49
|
+
> ℹ️ **Version-line note**: this plugin declares the peer range `dsh-better-sidebar@^0.17.0 || ^0.18.0`, and both lines are co-tested — **better-sidebar 0.18.0 + DSH 0.1.2-rc.1** (validated since 0.2.7) and **better-sidebar 0.17.1 + DSH 0.1.1-rc.x (stable)**. Note that better-sidebar 0.18.0 supports only DSH 0.1.2-rc.1+: on an older stable host (0.1.0-rc.8 ~ 0.1.1-rc.2), pin `dsh-better-sidebar@0.17.1`.
|
|
42
50
|
|
|
43
51
|
### Option 2: install from source (development / offline)
|
|
44
52
|
|
|
45
53
|
```sh
|
|
46
54
|
git clone https://github.com/Andor-Z/dsh-turn-outline
|
|
47
55
|
cd dsh-turn-outline
|
|
48
|
-
pnpm install && pnpm build && pnpm pack # produces dsh-turn-outline-0.2.
|
|
49
|
-
npx @deepseek-ai/dsh plugin --profile desktop add "file:$(pwd)/dsh-turn-outline-0.2.
|
|
56
|
+
pnpm install && pnpm build && pnpm pack # produces dsh-turn-outline-0.2.6.tgz
|
|
57
|
+
npx @deepseek-ai/dsh plugin --profile desktop add "file:$(pwd)/dsh-turn-outline-0.2.6.tgz"
|
|
50
58
|
```
|
|
51
59
|
|
|
52
60
|
### Restart & enable
|
|
@@ -70,7 +78,7 @@ npx @deepseek-ai/dsh plugin --profile desktop remove dsh-turn-outline
|
|
|
70
78
|
|
|
71
79
|
```
|
|
72
80
|
host (Node) inject: ['webServer','sessionQuery','sessions']
|
|
73
|
-
POST /
|
|
81
|
+
POST /turn-outline/api/events { sessionId, afterSeq }
|
|
74
82
|
→ live sessions: borrow the frozen snapshot of the live store (O(1) probe + O(delta) projection, zero deep clones)
|
|
75
83
|
→ cold sessions: sessionQuery persisted log + lastSeq cache (LRU + byte cap)
|
|
76
84
|
→ paged responses (PAGE_SIZE events per page, hasMore semantics) + field trimming (no full tool-result payloads)
|
|
@@ -91,7 +99,7 @@ client (browser) inject: ['sessions','betterSidebar'] (betterSidebar is an optio
|
|
|
91
99
|
|
|
92
100
|
## Permissions & data
|
|
93
101
|
|
|
94
|
-
- **Session logs**: one read-only host route `POST /
|
|
102
|
+
- **Session logs**: one read-only host route `POST /turn-outline/api/events`, browser same-origin + Host-header trust fence; read-only, never writes or mutates the session.
|
|
95
103
|
- **Zero LLM calls**: nothing is sent to any model; no token consumption.
|
|
96
104
|
- **No credential access**: no other network/file access besides session events; no user config, no persistent storage (tab state follows better-sidebar's layout persistence).
|
|
97
105
|
|
|
@@ -102,14 +110,14 @@ client (browser) inject: ['sessions','betterSidebar'] (betterSidebar is an optio
|
|
|
102
110
|
| Platform | DSH 0.1.1-rc.x (stable line; web/desktop profile) |
|
|
103
111
|
| Dependencies | dsh-better-sidebar (optional peer; inert when absent), react (injected by the DSH client seed) |
|
|
104
112
|
| Build | TypeScript + esbuild, no runtime npm dependencies |
|
|
105
|
-
| Tests | vitest,
|
|
113
|
+
| Tests | vitest, 80 cases (fold / folder / jump / tab / host) |
|
|
106
114
|
|
|
107
115
|
## Development
|
|
108
116
|
|
|
109
117
|
```sh
|
|
110
118
|
pnpm install # if pnpm 10+ blocks build scripts, allow esbuild as prompted (see pnpm-workspace.yaml)
|
|
111
119
|
pnpm typecheck # tsc --noEmit
|
|
112
|
-
pnpm test # vitest run (
|
|
120
|
+
pnpm test # vitest run (80 cases)
|
|
113
121
|
pnpm build # esbuild → lib/index.js + lib/client.js (includes bundle-contract self check)
|
|
114
122
|
pnpm pack # produces dsh-turn-outline-<version>.tgz (prepack rebuilds automatically)
|
|
115
123
|
```
|
package/README.md
CHANGED
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
[English](README.en.md)
|
|
11
11
|
|
|
12
|
+
## 样式预览
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<img src="docs/assets/turn-outline-panel.png" alt="轮次轨迹 tab:会话按用户轮次折叠成「输入 → 工具步骤 → 输出」的结构化轨迹" width="420" />
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
> 真实会话截图:整条会话折叠为「轮次 1-5」,每轮 = 用户输入 + 工具步骤 + 最终输出;顶部工具筛选 chips(全部 / bash / read / edit / 更多)按语义色点聚合,点击任意行可定位回对话原位。
|
|
19
|
+
|
|
12
20
|
## 概览
|
|
13
21
|
|
|
14
22
|
**适合谁**:使用 DeepSeek Harness(Web / Desktop profile)、会话很长、想快速回看某一步「当时做了什么」的用户——希望零成本、不引入 LLM 也不改动核心。
|
|
@@ -29,24 +37,24 @@
|
|
|
29
37
|
|
|
30
38
|
### 方式一:npm 一行命令(推荐)
|
|
31
39
|
|
|
32
|
-
先装 better-sidebar
|
|
40
|
+
先装 better-sidebar,再装本插件:
|
|
33
41
|
|
|
34
42
|
```sh
|
|
35
|
-
npx @deepseek-ai/dsh plugin --profile desktop add dsh-better-sidebar
|
|
43
|
+
npx @deepseek-ai/dsh plugin --profile desktop add dsh-better-sidebar
|
|
36
44
|
npx @deepseek-ai/dsh plugin --profile desktop add dsh-turn-outline
|
|
37
45
|
```
|
|
38
46
|
|
|
39
47
|
> 使用 Web profile 时把 `--profile desktop` 换成 `--profile web`。
|
|
40
48
|
>
|
|
41
|
-
>
|
|
49
|
+
> ℹ️ **版本线说明**:本插件 peer 声明 `dsh-better-sidebar@^0.17.0 || ^0.18.0`,双线均已联调——**better-sidebar 0.18.0 + DSH 0.1.2-rc.1**(0.2.7 起验证)与 **better-sidebar 0.17.1 + DSH 0.1.1-rc.x(stable)**。注意 better-sidebar 0.18.0 仅支持 DSH 0.1.2-rc.1+:旧 stable 宿主(0.1.0-rc.8 ~ 0.1.1-rc.2)请固定安装 `dsh-better-sidebar@0.17.1`。
|
|
42
50
|
|
|
43
51
|
### 方式二:源码本地安装(开发 / 离线)
|
|
44
52
|
|
|
45
53
|
```sh
|
|
46
54
|
git clone https://github.com/Andor-Z/dsh-turn-outline
|
|
47
55
|
cd dsh-turn-outline
|
|
48
|
-
pnpm install && pnpm build && pnpm pack # 产出 dsh-turn-outline-0.2.
|
|
49
|
-
npx @deepseek-ai/dsh plugin --profile desktop add "file:$(pwd)/dsh-turn-outline-0.2.
|
|
56
|
+
pnpm install && pnpm build && pnpm pack # 产出 dsh-turn-outline-0.2.6.tgz
|
|
57
|
+
npx @deepseek-ai/dsh plugin --profile desktop add "file:$(pwd)/dsh-turn-outline-0.2.6.tgz"
|
|
50
58
|
```
|
|
51
59
|
|
|
52
60
|
### 重启与启用
|
|
@@ -70,7 +78,7 @@ npx @deepseek-ai/dsh plugin --profile desktop remove dsh-turn-outline
|
|
|
70
78
|
|
|
71
79
|
```
|
|
72
80
|
host(Node) inject: ['webServer','sessionQuery','sessions']
|
|
73
|
-
POST /
|
|
81
|
+
POST /turn-outline/api/events { sessionId, afterSeq }
|
|
74
82
|
→ 活跃会话:直接借用 live store 冻结快照(O(1) 探测 + O(delta) 投影,零深拷贝)
|
|
75
83
|
→ 冷会话:sessionQuery 持久化日志 + lastSeq 缓存(LRU + 字节上限)
|
|
76
84
|
→ 响应逐页(每页 PAGE_SIZE 事件,hasMore 语义)+ 字段裁剪(不发 tool 结果全文)
|
|
@@ -91,7 +99,7 @@ client(浏览器) inject: ['sessions','betterSidebar'](betterSidebar 可
|
|
|
91
99
|
|
|
92
100
|
## 权限与数据
|
|
93
101
|
|
|
94
|
-
- **会话日志**:host 只读路由 `POST /
|
|
102
|
+
- **会话日志**:host 只读路由 `POST /turn-outline/api/events`,浏览器同源 + Host 头信任围栏;只读不回写、不改动会话。
|
|
95
103
|
- **零 LLM 调用**:不向任何模型发送内容,无 token 消耗。
|
|
96
104
|
- **不读取凭据**:除会话事件外无其他网络 / 文件访问;无用户配置、无持久化存储(tab 状态随 better-sidebar 布局持久化)。
|
|
97
105
|
|
|
@@ -102,14 +110,14 @@ client(浏览器) inject: ['sessions','betterSidebar'](betterSidebar 可
|
|
|
102
110
|
| 平台 | DSH 0.1.1-rc.x(stable 线;web/desktop profile) |
|
|
103
111
|
| 依赖 | dsh-better-sidebar(可选 peer;未装则 inert)、react(DSH client 种子注入) |
|
|
104
112
|
| 构建 | TypeScript + esbuild,无运行时 npm 依赖 |
|
|
105
|
-
| 测试 | vitest
|
|
113
|
+
| 测试 | vitest 80 用例(fold / folder / jump / tab / host) |
|
|
106
114
|
|
|
107
115
|
## 开发
|
|
108
116
|
|
|
109
117
|
```sh
|
|
110
118
|
pnpm install # pnpm 10+ 拦截构建脚本时按提示允许 esbuild(见 pnpm-workspace.yaml)
|
|
111
119
|
pnpm typecheck # tsc --noEmit
|
|
112
|
-
pnpm test # vitest run(
|
|
120
|
+
pnpm test # vitest run(80 用例)
|
|
113
121
|
pnpm build # esbuild → lib/index.js + lib/client.js(含 bundle 契约自检)
|
|
114
122
|
pnpm pack # 产出 dsh-turn-outline-<version>.tgz(prepack 自动重新构建)
|
|
115
123
|
```
|
package/lib/client.js
CHANGED
|
@@ -1107,7 +1107,7 @@ function t(lang, key) {
|
|
|
1107
1107
|
var React = __toESM(require("react"), 1);
|
|
1108
1108
|
var MAX_PULL_PAGES = 64;
|
|
1109
1109
|
function fetchTrailEvents(sessionId, afterSeq, base) {
|
|
1110
|
-
const url = new URL("/
|
|
1110
|
+
const url = new URL("/turn-outline/api/events", base ?? window.location.origin);
|
|
1111
1111
|
return fetch(url.toString(), {
|
|
1112
1112
|
method: "POST",
|
|
1113
1113
|
headers: { "content-type": "application/json", accept: "application/json" },
|
|
@@ -1134,15 +1134,23 @@ function useTrail(sessionId, visible, fetchEvents, sessionsService) {
|
|
|
1134
1134
|
const requestGen = React.useRef(0);
|
|
1135
1135
|
const lastPullAt = React.useRef(0);
|
|
1136
1136
|
const throttleTimer = React.useRef(null);
|
|
1137
|
+
const visibleRef = React.useRef(visible);
|
|
1138
|
+
const schedulePullRef = React.useRef(null);
|
|
1137
1139
|
React.useEffect(() => {
|
|
1138
1140
|
fetchRef.current = fetchEvents;
|
|
1139
1141
|
}, [fetchEvents]);
|
|
1142
|
+
React.useEffect(() => {
|
|
1143
|
+
visibleRef.current = visible;
|
|
1144
|
+
}, [visible]);
|
|
1140
1145
|
React.useEffect(() => () => {
|
|
1141
1146
|
if (throttleTimer.current !== null) window.clearTimeout(throttleTimer.current);
|
|
1147
|
+
schedulePullRef.current = null;
|
|
1142
1148
|
}, []);
|
|
1143
1149
|
const pull = React.useCallback(async () => {
|
|
1144
1150
|
const generation = ++requestGen.current;
|
|
1145
1151
|
const sid = sessionIdRef.current;
|
|
1152
|
+
const seqBefore = seqRef.current;
|
|
1153
|
+
let drainCapped = false;
|
|
1146
1154
|
try {
|
|
1147
1155
|
for (let page = 0; page < MAX_PULL_PAGES; page += 1) {
|
|
1148
1156
|
const { events, hasMore } = await fetchRef.current(sid, seqRef.current);
|
|
@@ -1163,10 +1171,17 @@ function useTrail(sessionId, visible, fetchEvents, sessionsService) {
|
|
|
1163
1171
|
setStats(folder.stats());
|
|
1164
1172
|
}
|
|
1165
1173
|
}
|
|
1166
|
-
if (hasMore !== true || events.length === 0)
|
|
1174
|
+
if (hasMore !== true || events.length === 0) {
|
|
1175
|
+
drainCapped = false;
|
|
1176
|
+
break;
|
|
1177
|
+
}
|
|
1178
|
+
drainCapped = page === MAX_PULL_PAGES - 1;
|
|
1167
1179
|
}
|
|
1168
1180
|
setError(null);
|
|
1169
1181
|
setStatus("ready");
|
|
1182
|
+
if (drainCapped && seqRef.current !== seqBefore && generation === requestGen.current && sid === sessionIdRef.current && visibleRef.current && schedulePullRef.current !== null) {
|
|
1183
|
+
schedulePullRef.current();
|
|
1184
|
+
}
|
|
1170
1185
|
} catch (reason) {
|
|
1171
1186
|
if (generation !== requestGen.current) return;
|
|
1172
1187
|
setError(reason instanceof Error ? reason.message : String(reason));
|
|
@@ -1192,6 +1207,9 @@ function useTrail(sessionId, visible, fetchEvents, sessionsService) {
|
|
|
1192
1207
|
void pull();
|
|
1193
1208
|
}, MIN_GAP - since);
|
|
1194
1209
|
}, [pull]);
|
|
1210
|
+
React.useEffect(() => {
|
|
1211
|
+
schedulePullRef.current = schedulePull;
|
|
1212
|
+
}, [schedulePull]);
|
|
1195
1213
|
React.useEffect(() => {
|
|
1196
1214
|
sessionIdRef.current = sessionId;
|
|
1197
1215
|
folderRef.current = null;
|
|
@@ -1809,28 +1827,27 @@ function targetIcon() {
|
|
|
1809
1827
|
}
|
|
1810
1828
|
|
|
1811
1829
|
// src/client/index.tsx
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
return listIcon();
|
|
1830
|
+
function routeIcon(size) {
|
|
1831
|
+
const children = [
|
|
1832
|
+
React3.createElement("circle", { key: "a", cx: 6, cy: 19, r: 3 }),
|
|
1833
|
+
React3.createElement("path", { key: "p", d: "M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15" }),
|
|
1834
|
+
React3.createElement("circle", { key: "b", cx: 18, cy: 5, r: 3 })
|
|
1835
|
+
];
|
|
1836
|
+
return React3.createElement(
|
|
1837
|
+
"svg",
|
|
1838
|
+
{
|
|
1839
|
+
viewBox: "0 0 24 24",
|
|
1840
|
+
width: size,
|
|
1841
|
+
height: size,
|
|
1842
|
+
fill: "none",
|
|
1843
|
+
stroke: "currentColor",
|
|
1844
|
+
strokeWidth: 2,
|
|
1845
|
+
strokeLinecap: "round",
|
|
1846
|
+
strokeLinejoin: "round",
|
|
1847
|
+
"aria-hidden": true
|
|
1848
|
+
},
|
|
1849
|
+
children
|
|
1850
|
+
);
|
|
1834
1851
|
}
|
|
1835
1852
|
var inject = ["sessions", "betterSidebar"];
|
|
1836
1853
|
function apply(ctx) {
|
|
@@ -1846,7 +1863,7 @@ function apply(ctx) {
|
|
|
1846
1863
|
ctx.effect(() => betterSidebar.registerTab({
|
|
1847
1864
|
id: "turn-outline",
|
|
1848
1865
|
title: () => typeof navigator !== "undefined" && navigator.language.toLowerCase().startsWith("zh") ? "\u8F6E\u6B21\u8F68\u8FF9" : "Turn Outline",
|
|
1849
|
-
icon: (size) =>
|
|
1866
|
+
icon: (size) => routeIcon(size),
|
|
1850
1867
|
order: 46,
|
|
1851
1868
|
single: true,
|
|
1852
1869
|
component: (props) => React3.createElement(TrailTab, {
|