dsh-plugin-yanyan-toolkit 0.0.0-stage → 1.2.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +29 -0
  3. package/README.i18n.yaml +6 -0
  4. package/README.md +65 -2
  5. package/README.zh.md +66 -0
  6. package/cordis.patch.yml +35 -0
  7. package/icon.svg +8 -0
  8. package/lib/client.js +26 -0
  9. package/lib/index.js +44 -0
  10. package/locale/en.json +6 -0
  11. package/locale/zh.json +6 -0
  12. package/package.json +79 -4
  13. package/packages/dsh-plugin-yanyan-subagent/README.md +72 -0
  14. package/packages/dsh-plugin-yanyan-subagent/README.zh.md +72 -0
  15. package/packages/dsh-plugin-yanyan-subagent/cordis.patch.yml +8 -0
  16. package/packages/dsh-plugin-yanyan-subagent/icon.svg +8 -0
  17. package/packages/dsh-plugin-yanyan-subagent/lib/client.js +1250 -0
  18. package/packages/dsh-plugin-yanyan-subagent/lib/index.js +951 -0
  19. package/packages/dsh-plugin-yanyan-subagent/lib/legacy-hidden-ids.json +9 -0
  20. package/packages/dsh-plugin-yanyan-subagent/lib/log-rewrite.js +382 -0
  21. package/packages/dsh-plugin-yanyan-subagent/lib/zstd-frames.js +81 -0
  22. package/packages/dsh-plugin-yanyan-subagent/locale/en.json +1 -0
  23. package/packages/dsh-plugin-yanyan-subagent/locale/zh.json +1 -0
  24. package/packages/dsh-plugin-yanyan-subagent/package.json +32 -0
  25. package/packages/dsh-plugin-yanyan-update/README.md +24 -0
  26. package/packages/dsh-plugin-yanyan-update/README.zh.md +24 -0
  27. package/packages/dsh-plugin-yanyan-update/cordis.patch.yml +7 -0
  28. package/packages/dsh-plugin-yanyan-update/icon.svg +5 -0
  29. package/packages/dsh-plugin-yanyan-update/lib/client.js +400 -0
  30. package/packages/dsh-plugin-yanyan-update/lib/index.js +840 -0
  31. package/packages/dsh-plugin-yanyan-update/locale/en.json +1 -0
  32. package/packages/dsh-plugin-yanyan-update/locale/zh.json +1 -0
  33. package/packages/dsh-plugin-yanyan-update/package.json +32 -0
  34. package/scripts/cleanup-ghost-subagents.mjs +241 -0
@@ -0,0 +1 @@
1
+ {"meta":{"title":"Plugin Update","description":"Check local and online plugin versions and update each"}}
@@ -0,0 +1 @@
1
+ {"meta":{"title":"插件更新","description":"检查本地与线上插件版本并逐个更新"}}
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "dsh-plugin-yanyan-update",
3
+ "version": "1.2.0",
4
+ "description": "插件更新:检查本地与线上插件版本并逐个更新",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "icon": "icon.svg",
8
+ "exports": {
9
+ ".": "./lib/index.js",
10
+ "./client": "./lib/client.js",
11
+ "./package.json": "./package.json",
12
+ "./locale/*.json": "./locale/*.json",
13
+ "./icon": "./icon.svg"
14
+ },
15
+ "files": ["lib/index.js","lib/client.js","cordis.patch.yml","icon.svg","locale/en.json","locale/zh.json","README.md","README.zh.md"],
16
+ "keywords": ["deepseek-harness","dsh-plugin","plugin-manager","plugin-update","cordis"],
17
+ "author": "sweet560",
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/sweet560/dsh-plugin-yanyan-toolkit.git",
22
+ "directory": "packages/dsh-plugin-yanyan-update"
23
+ },
24
+ "engines": { "node": "^22.19.0 || >=24.0.0" },
25
+ "scripts": { "test": "node --test test/*.test.mjs" },
26
+ "dsh": {
27
+ "engines": { "dsh": ">=0.2.2-rc.2" },
28
+ "bundle": { "patch": "./cordis.patch.yml" },
29
+ "client": { "inject": ["locale"], "platform": "web" },
30
+ "manifestVersion": 1
31
+ }
32
+ }
@@ -0,0 +1,241 @@
1
+ // 一次性清理:把本工作区里残留的 mnemon 虚拟委派幽灵会话彻底清掉。
2
+ //
3
+ // 背景:用户用「智能体管理」删除子智能体时,旧实现只做 archiveSession;
4
+ // subagentCatalog 是父会话日志的 append-only 投影,归档/删目录都不会移除
5
+ // 引用,于是列表里仍显示该条目;点进去则报 subagent/not-found。
6
+ //
7
+ // 本脚本做四件事(全部限定在 DSH_HOME 内,realpath 校验不越界):
8
+ // ① 删除 sessions/**/<ghost> 与 session-<ghost> 两种拼写的目录;
9
+ // ② 从所有 storages/session_projcache/sessions/*.json 的
10
+ // record.rows.subagentCatalog.val.head.values(含 previous 分片)里
11
+ // 剔除 childId 命中的条目;
12
+ // ③ 从 storages/workspace.json 的 global.archivedSessionIds 与各
13
+ // tables.workspaces[*].sessionIds 移除命中 id;
14
+ // ④ 从 dsh-session-archive/archive-ledger.json 与归档台账里移除命中 id;
15
+ // ⑤ 删除 storages/session_projcache/sessions/<id>.json 两种拼写。
16
+ //
17
+ // 用法:node scripts/cleanup-ghost-subagents.mjs [--dry-run] [--id <id> ...] [--all]
18
+ // 默认只清理「已归档但仍残留」的幽灵 id(workspace.json 的
19
+ // global.archivedSessionIds 里以 mnemon- 开头的条目)——这正是用户截图里
20
+ // 那 3 条;加 --all 才扫描全部历史 mnemon 委派(谨慎,会动别的工作区历史)。
21
+ // --id 可多次指定精确 id。
22
+
23
+ import {
24
+ existsSync, readFileSync, readdirSync, realpathSync, rmSync, statSync,
25
+ writeFileSync, renameSync, unlinkSync,
26
+ } from 'node:fs';
27
+ import { dirname, join, resolve, sep } from 'node:path';
28
+ import { fileURLToPath } from 'node:url';
29
+
30
+ const HERE = dirname(fileURLToPath(import.meta.url));
31
+ const DSH_HOME = process.env.DSH_HOME || resolve(HERE, '..', '..', '..');
32
+ const DRY = process.argv.includes('--dry-run');
33
+ const ALL = process.argv.includes('--all');
34
+
35
+ function core(id) { return String(id || '').replace(/^session-/, ''); }
36
+ function spellings(id) {
37
+ const c = core(id);
38
+ return [c, 'session-' + c];
39
+ }
40
+ function isInside(root, child) {
41
+ const r = resolve(root), c = resolve(child);
42
+ return c === r || c.startsWith(r + sep);
43
+ }
44
+ function readJson(p) { return JSON.parse(readFileSync(p, 'utf8')); }
45
+ function writeJsonAtomic(p, obj) {
46
+ const tmp = p + '.yanyan.tmp';
47
+ writeFileSync(tmp, JSON.stringify(obj), 'utf8');
48
+ renameSync(tmp, p);
49
+ }
50
+
51
+ /** 收集所有要清理的幽灵 id(默认只取已归档残留,--all 才全量,--id 精确指定)。 */
52
+ function collectGhostIds() {
53
+ const ids = new Set();
54
+ for (let i = 0; i < process.argv.length; i++) {
55
+ if (process.argv[i] === '--id' && process.argv[i + 1]) ids.add(process.argv[i + 1]);
56
+ }
57
+ const hasExplicit = ids.size > 0;
58
+ // 默认范围:workspace.json 里「已归档」的 mnemon 虚拟会话。
59
+ if (!hasExplicit) {
60
+ const wp = join(DSH_HOME, 'storages', 'workspace.json');
61
+ if (existsSync(wp)) {
62
+ try {
63
+ const doc = readJson(wp);
64
+ for (const id of doc?.global?.archivedSessionIds || []) {
65
+ if (typeof id === 'string' && /^mnemon-/.test(core(id))) ids.add(id);
66
+ }
67
+ // 各 workspace 成员里若也残留同样拼写,一并纳入。
68
+ for (const w of Object.values(doc?.tables?.workspaces || {})) {
69
+ for (const id of w?.sessionIds || []) {
70
+ if (typeof id === 'string' && /^mnemon-/.test(core(id)) && ids.size > 0) {
71
+ // 只把与已归档集合里同名(去前缀后相等)的加进来,避免扩大范围。
72
+ const c = core(id);
73
+ if ([...ids].some((g) => core(g) === c)) ids.add(id);
74
+ }
75
+ }
76
+ }
77
+ } catch { /* 损坏文件跳过 */ }
78
+ }
79
+ }
80
+ // 只有 --all 才做全量历史扫描;默认范围与 --id 精确指定都保持窄范围,
81
+ // 避免误删别的工作区的正常历史记录。
82
+ if (!ALL) return [...ids];
83
+ const pdir = join(DSH_HOME, 'storages', 'session_projcache', 'sessions');
84
+ if (existsSync(pdir)) {
85
+ for (const f of readdirSync(pdir)) {
86
+ if (!f.endsWith('.json')) continue;
87
+ try {
88
+ const rec = readJson(join(pdir, f));
89
+ const head = rec?.record?.rows?.subagentCatalog?.val?.head;
90
+ if (!head) continue;
91
+ const chunks = [];
92
+ for (let c = head; c; c = c.previous) chunks.push(c);
93
+ for (const c of chunks) {
94
+ for (const v of c.values || []) {
95
+ const cid = v && (v.childId || v.id);
96
+ if (typeof cid === 'string' && /^mnemon-/.test(core(cid))) ids.add(cid);
97
+ }
98
+ }
99
+ } catch { /* 损坏文件跳过 */ }
100
+ }
101
+ }
102
+ const sroot = join(DSH_HOME, 'sessions');
103
+ if (existsSync(sroot)) {
104
+ for (const proj of readdirSync(sroot)) {
105
+ const pp = join(sroot, proj);
106
+ if (!statSync(pp).isDirectory()) continue;
107
+ for (const seg of readdirSync(pp)) {
108
+ if (/^mnemon-/.test(core(seg))) ids.add(seg);
109
+ }
110
+ }
111
+ }
112
+ return [...ids];
113
+ }
114
+
115
+ function matches(id, ghostSet) {
116
+ const c = core(id);
117
+ return ghostSet.has(c) || ghostSet.has('session-' + c) ||
118
+ [...ghostSet].some((g) => core(g) === c);
119
+ }
120
+
121
+ /** 从 chunked-list head 的 values(含 previous 分片)里剔除命中条目。 */
122
+ function stripCatalog(head, ghostSet) {
123
+ let removed = 0;
124
+ for (let c = head; c; c = c.previous) {
125
+ if (!Array.isArray(c.values)) continue;
126
+ const next = c.values.filter((v) => {
127
+ const cid = v && (v.childId || v.id);
128
+ const hit = typeof cid === 'string' && matches(cid, ghostSet);
129
+ if (hit) removed += 1;
130
+ return !hit;
131
+ });
132
+ c.values = next;
133
+ }
134
+ return removed;
135
+ }
136
+
137
+ function cleanSessionsDirs(ghostSet, report) {
138
+ const sroot = join(DSH_HOME, 'sessions');
139
+ if (!existsSync(sroot)) return;
140
+ const rootReal = realpathSync(sroot);
141
+ for (const proj of readdirSync(sroot)) {
142
+ const pp = join(sroot, proj);
143
+ if (!statSync(pp).isDirectory()) continue;
144
+ for (const seg of readdirSync(pp)) {
145
+ if (!matches(seg, ghostSet)) continue;
146
+ const dir = join(pp, seg);
147
+ const real = realpathSync(dir);
148
+ if (!isInside(rootReal, real)) { report.skipped.push(dir + ' (逃逸)'); continue; }
149
+ report.dirs.push(dir);
150
+ if (!DRY) rmSync(real, { recursive: true, force: true });
151
+ }
152
+ }
153
+ }
154
+
155
+ function cleanProjcache(ghostSet, report) {
156
+ const pdir = join(DSH_HOME, 'storages', 'session_projcache', 'sessions');
157
+ if (!existsSync(pdir)) return;
158
+ for (const f of readdirSync(pdir)) {
159
+ if (!f.endsWith('.json')) continue;
160
+ const p = join(pdir, f);
161
+ const rec = readJson(p);
162
+ const row = rec?.record?.rows?.subagentCatalog;
163
+ if (row && row.val && row.val.head) {
164
+ const n = stripCatalog(row.val.head, ghostSet);
165
+ if (n > 0) {
166
+ report.catalog.push(f + ' (-' + n + ')');
167
+ if (!DRY) writeJsonAtomic(p, rec);
168
+ }
169
+ }
170
+ // 幽灵会话自己的 projcache 文件
171
+ if (matches(f.replace(/\.json$/, ''), ghostSet)) {
172
+ report.files.push(p);
173
+ if (!DRY) unlinkSync(p);
174
+ }
175
+ }
176
+ // 聚合索引(若存在)
177
+ const idx = join(DSH_HOME, 'storages', 'session_projcache.json');
178
+ if (existsSync(idx)) {
179
+ try {
180
+ const doc = readJson(idx);
181
+ const t = doc?.tables?.sessions;
182
+ if (t) {
183
+ let n = 0;
184
+ for (const k of Object.keys(t)) {
185
+ if (matches(k, ghostSet)) { delete t[k]; n += 1; }
186
+ }
187
+ if (n > 0) { report.index = n; if (!DRY) writeJsonAtomic(idx, doc); }
188
+ }
189
+ } catch { /* 损坏索引跳过 */ }
190
+ }
191
+ }
192
+
193
+ function cleanWorkspaceJson(ghostSet, report) {
194
+ const p = join(DSH_HOME, 'storages', 'workspace.json');
195
+ if (!existsSync(p)) return;
196
+ const doc = readJson(p);
197
+ let changed = false;
198
+ const g = doc?.global;
199
+ if (g && Array.isArray(g.archivedSessionIds)) {
200
+ const next = g.archivedSessionIds.filter((id) => !matches(id, ghostSet));
201
+ report.archived += g.archivedSessionIds.length - next.length;
202
+ if (next.length !== g.archivedSessionIds.length) { g.archivedSessionIds = next; changed = true; }
203
+ }
204
+ const tables = doc?.tables?.workspaces;
205
+ if (tables) {
206
+ for (const w of Object.values(tables)) {
207
+ if (!Array.isArray(w.sessionIds)) continue;
208
+ const next = w.sessionIds.filter((id) => !matches(id, ghostSet));
209
+ report.members += w.sessionIds.length - next.length;
210
+ if (next.length !== w.sessionIds.length) { w.sessionIds = next; changed = true; }
211
+ }
212
+ }
213
+ if (changed && !DRY) writeJsonAtomic(p, doc);
214
+ }
215
+
216
+ function cleanLedger(ghostSet, report) {
217
+ const p = join(DSH_HOME, 'dsh-session-archive', 'archive-ledger.json');
218
+ if (!existsSync(p)) return;
219
+ const doc = readJson(p);
220
+ const e = doc?.entries;
221
+ if (!e || typeof e !== 'object') return;
222
+ let n = 0;
223
+ for (const k of Object.keys(e)) {
224
+ if (matches(k, ghostSet)) { delete e[k]; n += 1; }
225
+ }
226
+ if (n > 0) { report.ledger = n; if (!DRY) writeJsonAtomic(p, doc); }
227
+ }
228
+
229
+ const ghosts = collectGhostIds();
230
+ const set = new Set(ghosts);
231
+ const report = { dirs: [], catalog: [], files: [], archived: 0, members: 0, ledger: 0, index: 0, skipped: [] };
232
+ console.log('[清理] DSH_HOME =', DSH_HOME);
233
+ console.log('[清理] 模式 =', DRY ? 'dry-run(不写盘)' : '实际执行');
234
+ console.log('[清理] 发现幽灵 id ' + set.size + ' 个:', ghosts.slice(0, 20));
235
+ if (set.size === 0) { console.log('[清理] 没有需要清理的条目。'); process.exit(0); }
236
+ cleanSessionsDirs(set, report);
237
+ cleanProjcache(set, report);
238
+ cleanWorkspaceJson(set, report);
239
+ cleanLedger(set, report);
240
+ console.log('[清理] 结果:', JSON.stringify(report, null, 2));
241
+ console.log('[清理] 完成。建议重启 DSH 服务后刷新页面,确认列表里不再出现这些条目。');