dsh-ws-recency 0.11.0 → 0.11.2
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 +4 -2
- package/client.js +9 -2
- package/index.js +34 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,9 +88,11 @@ DSH 原生的置顶只把会话顶到**它所属工作区分组的第一行**,
|
|
|
88
88
|
3. 把旧日志目录移进 `.trash/ws-recency/<id>-move-<时间戳>`(既是备份,也腾出这个 id);
|
|
89
89
|
4. 用新 `cwd`、原 id 重建日志,再回放全部事件;
|
|
90
90
|
5. 校验新 header 的 cwd;
|
|
91
|
-
6.
|
|
91
|
+
6. 把新 cwd 同步到工作区的 header 索引和这个会话的内存对象(否则宿主会用旧目录拒绝 attach),再 attach 到目标(失败就回滚原目录),最后 detach 旧工作区;
|
|
92
92
|
7. 删投影缓存,让它按新路径重建。
|
|
93
93
|
|
|
94
|
+
**只移动会话**:对话框里明确提示——它在旧工作区里创建/修改的文件不会跟着走,仍留在原目录(附件与 rewind 快照按会话 id 存,不受影响)。
|
|
95
|
+
|
|
94
96
|
**风险与退路**:这是插件里唯一改写会话存储的操作。备份会保留,回滚 = 把备份目录移回 `sessions/<project>/<id>`。
|
|
95
97
|
|
|
96
98
|
## 设置
|
|
@@ -128,7 +130,7 @@ dsh plugin --profile <profile> add github:chyxhtc/dsh-ws-recency
|
|
|
128
130
|
或固定版本的预构建包:
|
|
129
131
|
|
|
130
132
|
```powershell
|
|
131
|
-
dsh plugin --profile <profile> add https://github.com/chyxhtc/dsh-ws-recency/releases/download/v0.11.
|
|
133
|
+
dsh plugin --profile <profile> add https://github.com/chyxhtc/dsh-ws-recency/releases/download/v0.11.2/dsh-ws-recency-0.11.2.tgz
|
|
132
134
|
```
|
|
133
135
|
|
|
134
136
|
装完把 `dsh-ws-recency` 加进 `~/.hypers-work/profiles/<profile>/package.json` 的 `dsh.profile.bundles`,重启应用。
|
package/client.js
CHANGED
|
@@ -123,6 +123,7 @@
|
|
|
123
123
|
".wsr-stats-legend{display:flex;flex-wrap:wrap;gap:10px}",
|
|
124
124
|
".wsr-stats-legend-item{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--dsw-alias-label-tertiary)}",
|
|
125
125
|
".wsr-stats-swatch{width:8px;height:8px;border-radius:2px}",
|
|
126
|
+
".wsr-move-warning{color:var(--dsw-alias-state-warn-primary,#d9a72c);font-size:11px;line-height:1.5}",
|
|
126
127
|
".wsr-move-list{display:flex;flex-direction:column;gap:4px}",
|
|
127
128
|
".wsr-move-row{display:flex;align-items:center;gap:8px;text-align:left;border:none;background:var(--dsw-alias-bg-layer-2,rgba(255,255,255,.04));color:inherit;font:inherit;padding:8px 10px;border-radius:8px;cursor:pointer}",
|
|
128
129
|
".wsr-move-row:hover:not([disabled]){background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.08))}",
|
|
@@ -205,11 +206,13 @@
|
|
|
205
206
|
"stats.diagnose.noHost": "\n\n(宿主半未加载:需要重启应用加载新版本)",
|
|
206
207
|
"menu.move": "移动到工作区…",
|
|
207
208
|
"move.title": "移动到工作区",
|
|
209
|
+
"move.warning": "只移动会话本身:它在旧工作区里创建或修改的文件不会跟着走,仍留在原目录。",
|
|
208
210
|
"move.note": "会话属于哪个工作区,由它创建时的目录决定;移动会改写会话记录的 cwd,把日志搬到新工作区目录,旧日志备份在 .trash/ws-recency。",
|
|
209
|
-
"move.confirm": "把会话移动到「{workspace}
|
|
211
|
+
"move.confirm": "把会话移动到「{workspace}」?\n\n只移动会话记录,旧工作区里的文件不会跟着搬。",
|
|
210
212
|
"move.current": "当前",
|
|
211
213
|
"move.working": "移动中…(会先停止会话)",
|
|
212
214
|
"move.done": "已移动到「{workspace}」。若列表没刷新,切换一下工作区或重启应用。",
|
|
215
|
+
"move.doneLive": "\n\n这个会话刚才是打开状态:建议重启应用后再继续对话,避免它沿用旧的目录。",
|
|
213
216
|
"move.failed": "移动失败:",
|
|
214
217
|
"stats.chart": "消耗分布",
|
|
215
218
|
"stats.details.show": "显示明细 ▾",
|
|
@@ -257,11 +260,13 @@
|
|
|
257
260
|
"stats.diagnose.noHost": "\n\n(the host half is not loaded: restart the app to pick up the new version)",
|
|
258
261
|
"menu.move": "Move to workspace\u2026",
|
|
259
262
|
"move.title": "Move to workspace",
|
|
263
|
+
"move.warning": "Only the session moves: files it created or changed in the old workspace stay where they are.",
|
|
260
264
|
"move.note": "A session belongs to the workspace of the directory it was created in. Moving rewrites the stored cwd, relocates the log and keeps the old one as a backup in .trash/ws-recency.",
|
|
261
|
-
"move.confirm": "Move this session to \"{workspace}\"
|
|
265
|
+
"move.confirm": "Move this session to \"{workspace}\"?\n\nOnly the session record moves; its files stay in the old workspace.",
|
|
262
266
|
"move.current": "current",
|
|
263
267
|
"move.working": "Moving\u2026 (the session is stopped first)",
|
|
264
268
|
"move.done": "Moved to \"{workspace}\". If the list has not refreshed, switch workspaces or restart the app.",
|
|
269
|
+
"move.doneLive": "\n\nThe session was open: restart the app before continuing it so it does not keep using the old directory.",
|
|
265
270
|
"move.failed": "Move failed: ",
|
|
266
271
|
"stats.chart": "Where the tokens went",
|
|
267
272
|
"stats.details.show": "Show details",
|
|
@@ -1406,6 +1411,7 @@
|
|
|
1406
1411
|
var body = statsDiv("wsr-modal-body");
|
|
1407
1412
|
panel.appendChild(body);
|
|
1408
1413
|
body.appendChild(statsDiv("wsr-stats-note", t("move.note")));
|
|
1414
|
+
body.appendChild(statsDiv("wsr-move-warning", t("move.warning")));
|
|
1409
1415
|
|
|
1410
1416
|
var current = workspaceOfSession(sessionId);
|
|
1411
1417
|
var items = (data.workspaces && data.workspaces.items) || [];
|
|
@@ -1443,6 +1449,7 @@
|
|
|
1443
1449
|
.then(function (result) {
|
|
1444
1450
|
if (result.json !== null && result.json.ok === true) {
|
|
1445
1451
|
body.textContent = t("move.done", { workspace: label });
|
|
1452
|
+
if (result.json.live === true) body.textContent += t("move.doneLive");
|
|
1446
1453
|
return;
|
|
1447
1454
|
}
|
|
1448
1455
|
var reason = (result.json !== null && result.json.error) || (result.text || "").trim().slice(0, 160) || "(empty response)";
|
package/index.js
CHANGED
|
@@ -545,6 +545,39 @@ export async function moveSessionToWorkspace(ctx, sessionId, workspaceId) {
|
|
|
545
545
|
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
546
546
|
}
|
|
547
547
|
|
|
548
|
+
/* The moved cwd lives in three places: the log (just rewritten), the Workspace
|
|
549
|
+
registry's header index, and the live Session object when this process still
|
|
550
|
+
has the session open. The registry validates against its own view, so both the
|
|
551
|
+
cached header and the live one have to follow - otherwise an open session is
|
|
552
|
+
refused with its old directory. */
|
|
553
|
+
const movedHeader = { ...header, cwd: targetPath };
|
|
554
|
+
const wasLive = typeof ctx.sessions?.get === "function" && ctx.sessions.get(sessionId) !== undefined;
|
|
555
|
+
try {
|
|
556
|
+
if (typeof ctx.workspaceRegistry.indexHeader === "function") await ctx.workspaceRegistry.indexHeader(movedHeader);
|
|
557
|
+
} catch {
|
|
558
|
+
/* the index rebuilds from disk on its own */
|
|
559
|
+
}
|
|
560
|
+
try {
|
|
561
|
+
const live = typeof ctx.sessions?.get === "function" ? ctx.sessions.get(sessionId) : undefined;
|
|
562
|
+
if (live !== undefined && live !== null && live.header !== undefined) live.header = movedHeader;
|
|
563
|
+
} catch {
|
|
564
|
+
/* an immutable live header: the attach below reports it */
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
let seenCwd = targetPath;
|
|
568
|
+
try {
|
|
569
|
+
if (typeof ctx.workspaceRegistry.readSessionHeader === "function") {
|
|
570
|
+
const seen = await ctx.workspaceRegistry.readSessionHeader(sessionId);
|
|
571
|
+
if (seen !== undefined && seen !== null && typeof seen.cwd === "string") seenCwd = seen.cwd;
|
|
572
|
+
}
|
|
573
|
+
} catch {
|
|
574
|
+
seenCwd = targetPath;
|
|
575
|
+
}
|
|
576
|
+
if (seenCwd !== targetPath) {
|
|
577
|
+
await rollback();
|
|
578
|
+
return { ok: false, error: `the app still holds this session's old directory (${seenCwd}); restart the app and move it again` };
|
|
579
|
+
}
|
|
580
|
+
|
|
548
581
|
/* Attach first, detach second: a refused attach must not orphan the session. */
|
|
549
582
|
try {
|
|
550
583
|
await target.attachSession(sessionId);
|
|
@@ -573,7 +606,7 @@ export async function moveSessionToWorkspace(ctx, sessionId, workspaceId) {
|
|
|
573
606
|
/* the cache is derived data */
|
|
574
607
|
}
|
|
575
608
|
|
|
576
|
-
return { ok: true, workspace: target.id, path: targetPath, detached, backup };
|
|
609
|
+
return { ok: true, workspace: target.id, path: targetPath, detached, backup, live: wasLive };
|
|
577
610
|
}
|
|
578
611
|
|
|
579
612
|
/** POST { sessionId, workspaceId } -> move that session to another workspace. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ws-recency",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "Sidebar session management for DeepSeek Harness: workspace groups sorted by latest activity, stale history folded, a global pinned section, follow-up marks, session delete into a recoverable trash, per-session token statistics split by tool, and moving a session to another workspace.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "chyxhtc",
|