dsh-perf-boost 0.1.0 → 0.2.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/lib/index.js CHANGED
@@ -11,6 +11,7 @@
11
11
  // 路由只读、无副作用:只取 `latest` 标签的版本号,不下载、不安装、不落盘。
12
12
  // 网络不可用时返回 `latest: null`,页面显示「未查到」,其余功能不受影响。
13
13
  import { readFile, writeFile, mkdir } from 'node:fs/promises';
14
+ import https from 'node:https';
14
15
  import { homedir } from 'node:os';
15
16
  import { join } from 'node:path';
16
17
 
@@ -25,11 +26,17 @@ const LAST_ROUTE = '/api/dsh-perf-boost/last-run';
25
26
  // 页面每次启动端口都不一样(localhost:随机端口),localStorage 跟着端口走,
26
27
  // 所以要跨启动留一份「上次实测」只能落到宿主侧的文件里。
27
28
  const LAST_FILE = join(homedir(), '.dsh', 'perf-boost-last.json');
29
+ // 大会话预热开关:客户端只在设置页里改,宿主要能持久化,重启后仍然是你的选择。
30
+ const WARM_ROUTE = '/api/dsh-perf-boost/warmup';
31
+ const WARM_FILE = join(homedir(), '.dsh', 'perf-boost-warmup.json');
28
32
  const PACKAGE_NAME = 'dsh-perf-boost';
29
33
  // npmmirror 优先(国内快),失败再问官方源。
30
34
  const REGISTRIES = ['https://registry.npmmirror.com', 'https://registry.npmjs.org'];
31
35
  const CACHE_MS = 10 * 60 * 1000;
32
- const TIMEOUT_MS = 8000;
36
+ const TIMEOUT_MS = 5000;
37
+ // 复用连接:第一次握手 200ms 上下,之后 40ms 上下。宿主里的全局 fetch 首次常常
38
+ // 要 3-4 秒(undici 独立连接池 + 冷 DNS),本机实测差 20 倍。
39
+ const AGENT = new https.Agent({ keepAlive: true, maxSockets: 4 });
33
40
 
34
41
  let cache = { at: 0, latest: null, source: '', error: '' };
35
42
 
@@ -60,24 +67,46 @@ export function isNewer(candidate, current) {
60
67
  return false;
61
68
  }
62
69
 
63
- async function fetchLatest(registry) {
64
- const url = `${registry}/${PACKAGE_NAME}/latest`;
65
- const response = await fetch(url, {
66
- headers: { accept: 'application/json' },
67
- signal: AbortSignal.timeout(TIMEOUT_MS),
70
+ function httpsJson(url) {
71
+ return new Promise((resolve, reject) => {
72
+ const request = https.get(url, { agent: AGENT, family: 4, headers: { accept: 'application/json' } }, (response) => {
73
+ const status = response.statusCode ?? 0;
74
+ if (status !== 200) {
75
+ response.resume();
76
+ reject(new Error(`HTTP ${status}`));
77
+ return;
78
+ }
79
+ const chunks = [];
80
+ response.on('data', (chunk) => chunks.push(chunk));
81
+ response.on('end', () => {
82
+ try {
83
+ resolve(JSON.parse(Buffer.concat(chunks).toString('utf8')));
84
+ } catch {
85
+ reject(new Error('响应不是 JSON'));
86
+ }
87
+ });
88
+ response.on('error', reject);
89
+ });
90
+ request.setTimeout(TIMEOUT_MS, () => request.destroy(new Error('timeout')));
91
+ request.on('error', reject);
68
92
  });
69
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
70
- const body = await response.json();
93
+ }
94
+
95
+ async function fetchLatest(registry) {
96
+ const body = await httpsJson(`${registry}/${PACKAGE_NAME}/latest`);
71
97
  const version = body?.version;
72
98
  if (typeof version !== 'string' || version === '') throw new Error('响应里没有 version');
73
99
  return { version, source: registry };
74
100
  }
75
101
 
76
- async function resolveLatest() {
102
+ async function resolveLatest(force = false) {
77
103
  const now = Date.now();
78
- if (cache.at !== 0 && now - cache.at < CACHE_MS) return cache;
104
+ if (!force && cache.at !== 0 && cache.at !== undefined && now - cache.at < CACHE_MS) return cache;
79
105
  let lastError = '未连接';
80
106
  let notPublished = false;
107
+ // 先问第一个源,拿到就回;失败才轮到下一个。
108
+ // 之前是「两边都问、取更高的版本」,多花一次跨洋往返(本机实测 2.0-2.5 秒),
109
+ // 为的只是防止镜像比官方慢半拍;版本号差半分钟不影响点「检查更新」这件事。
81
110
  for (const registry of REGISTRIES) {
82
111
  try {
83
112
  const { version, source } = await fetchLatest(registry);
@@ -85,26 +114,76 @@ async function resolveLatest() {
85
114
  return cache;
86
115
  } catch (error) {
87
116
  lastError = error?.message ?? String(error);
88
- // 两个镜像都 404 = 这个包还没发到 npm,不算「网络失败」。
89
- if (lastError === 'HTTP 404') notPublished = true;
90
- else notPublished = false;
117
+ // 这个源说 404 = 它上面没有这个包,换下一个源继续试。
118
+ notPublished = lastError === 'HTTP 404';
91
119
  }
92
120
  }
93
121
  cache = { at: now, latest: null, source: '', error: notPublished ? 'not-published' : lastError };
94
122
  return cache;
95
123
  }
96
124
 
125
+ async function readWarmSetting() {
126
+ try {
127
+ const parsed = JSON.parse(await readFile(WARM_FILE, 'utf8'));
128
+ if (typeof parsed?.enabled === 'boolean') return parsed.enabled;
129
+ } catch {
130
+ // 没有文件 / 文件坏了:用默认值(开)
131
+ }
132
+ return true;
133
+ }
134
+
135
+ async function writeWarmSetting(enabled) {
136
+ try {
137
+ await mkdir(join(homedir(), '.dsh'), { recursive: true });
138
+ await writeFile(WARM_FILE, JSON.stringify({ enabled: !!enabled }), 'utf8');
139
+ } catch {
140
+ // 写不进去只影响「下次启动还记得吗」,本次开关照旧生效
141
+ }
142
+ }
143
+
97
144
  export function apply(ctx, config) {
98
145
  // 大会话预热(宿主侧,可关):把投影冷折叠从首屏挪到后台分片做。
99
146
  // 结构不认识时它会自己静默退出,不影响下面这条路由与客户端功能。
100
- try {
101
- const logger = typeof ctx.logger === 'function' ? ctx.logger('perf-boost') : { info() {}, warn: console.warn };
102
- if (!config || config.warmup !== false) {
103
- const warm = createWarmup({ ctx, logger, config: config || {} });
147
+ let warm = null;
148
+ let warmSetting = true; // 落盘的开关状态,和 warm 实例分开记
149
+ const warmBlocked = config?.warmup === false;
150
+ const warmLogger = (() => {
151
+ try {
152
+ return typeof ctx.logger === 'function' ? ctx.logger('perf-boost') : { info() {}, warn: console.warn };
153
+ } catch {
154
+ return { info() {}, warn: console.warn };
155
+ }
156
+ })();
157
+
158
+ function startWarm() {
159
+ if (warmBlocked || warm !== null) return;
160
+ try {
161
+ warm = createWarmup({ ctx, logger: warmLogger, config: config || {} });
104
162
  warm.start();
163
+ } catch (error) {
164
+ warm = null;
165
+ try { console.warn('[perf-boost] warmup 装配失败:', error?.message ?? error); } catch { void 0; }
105
166
  }
106
- } catch (error) {
107
- try { console.warn('[perf-boost] warmup 装配失败:', error?.message ?? error); } catch { void 0; }
167
+ }
168
+
169
+ function stopWarm() {
170
+ if (warm === null) return;
171
+ try { warm.stop(); } catch { void 0; }
172
+ warm = null;
173
+ }
174
+
175
+ if (!warmBlocked) {
176
+ readWarmSetting()
177
+ .then((on) => { warmSetting = on; if (on) startWarm(); })
178
+ .catch(() => { void 0; });
179
+ }
180
+
181
+ // 启动后先在后台问一次,设置页打开时直接命中缓存(失败无所谓,路由里还会再试)。
182
+ try {
183
+ const warmTimer = setTimeout(() => { resolveLatest().catch(() => { void 0; }); }, 3000);
184
+ if (typeof warmTimer?.unref === 'function') warmTimer.unref();
185
+ } catch {
186
+ // 拿不到定时器就算了,只是第一次打开设置页慢一点。
108
187
  }
109
188
 
110
189
  ctx.inject(['connection'], (c) => {
@@ -160,8 +239,47 @@ export function apply(ctx, config) {
160
239
  // 注册失败只是少一条缓存读取,面板会退回到只显示本次数据。
161
240
  }
162
241
 
163
- const handler = async () => {
164
- const [installed, resolved] = await Promise.all([readInstalledVersion(), resolveLatest()]);
242
+ // 预热开关:读/写都走这一条,写会落盘,重启后保持。
243
+ const warmHandler = async (request) => {
244
+ let body = null;
245
+ try {
246
+ const text = typeof request?.text === 'function' ? await request.text() : '';
247
+ if (text) body = JSON.parse(text);
248
+ } catch {
249
+ body = null;
250
+ }
251
+ if (body && typeof body.enabled === 'boolean') {
252
+ warmSetting = body.enabled;
253
+ await writeWarmSetting(body.enabled);
254
+ if (body.enabled) startWarm();
255
+ else stopWarm();
256
+ } else {
257
+ warmSetting = await readWarmSetting();
258
+ if (!warmBlocked && warmSetting && warm === null) startWarm();
259
+ }
260
+ return Response.json({ ok: true, value: { enabled: warmSetting && !warmBlocked } }, { headers: { 'cache-control': 'no-store' } });
261
+ };
262
+
263
+ try {
264
+ c.effect(
265
+ () => register({ path: WARM_ROUTE, methods: ['POST'], requestBody: 'buffered', fetch: warmHandler }),
266
+ 'dsh-perf-boost: /api/dsh-perf-boost/warmup',
267
+ );
268
+ } catch {
269
+ // 注册失败只是设置页那个开关不出现,预热仍按配置文件走。
270
+ }
271
+
272
+ const handler = async (request) => {
273
+ let force = false;
274
+ try {
275
+ const text = typeof request?.text === 'function' ? await request.text() : '';
276
+ // 手动点「检查更新」要拿最新答案,不能被 10 分钟缓存挡住;自动检查仍走缓存。
277
+ if (text) force = JSON.parse(text)?.force === true;
278
+ } catch {
279
+ force = false;
280
+ }
281
+ const startedAt = Date.now();
282
+ const [installed, resolved] = await Promise.all([readInstalledVersion(), resolveLatest(force)]);
165
283
  const latest = resolved.latest;
166
284
  return Response.json(
167
285
  {
@@ -173,6 +291,7 @@ export function apply(ctx, config) {
173
291
  source: resolved.source,
174
292
  checkedAt: new Date(resolved.at === 0 ? Date.now() : resolved.at).toISOString(),
175
293
  error: latest === null ? resolved.error : '',
294
+ elapsedMs: Date.now() - startedAt,
176
295
  },
177
296
  },
178
297
  { headers: { 'cache-control': 'no-store' } },
package/package.json CHANGED
@@ -1,56 +1,67 @@
1
- {
2
- "name": "dsh-perf-boost",
3
- "version": "0.1.0",
4
- "description": "DSH 桌面端性能优化(纯客户端):长对话省渲染、窗口隐藏不排帧默认开,关背景模糊/减少动画/空闲降帧按需打开,设置页显示实测省下的主线程时间。Client-side rendering optimizations for DeepSeek Harness Desktop.",
5
- "type": "module",
6
- "main": "lib/index.js",
7
- "exports": {
8
- ".": "./lib/index.js",
9
- "./client": "./lib/client.js"
10
- },
11
- "license": "MIT",
12
- "keywords": [
13
- "deepseek-harness",
14
- "dsh",
15
- "dsh-plugin",
16
- "performance",
17
- "fps",
18
- "requestanimationframe",
19
- "content-visibility"
20
- ],
21
- "files": [
22
- "lib",
23
- "assets",
24
- "cordis.patch.yml",
25
- "README.md",
26
- "LICENSE",
27
- "CHANGELOG.md"
28
- ],
29
- "engines": {
30
- "dsh": ">=0.2.0-0"
31
- },
32
- "peerDependencies": {
33
- "@deepseek-ai/cordis": "^4.0.1"
34
- },
35
- "repository": {
36
- "type": "git",
37
- "url": "git+https://github.com/AlLHHH/dsh-perf-boost.git"
38
- },
39
- "homepage": "https://github.com/AlLHHH/dsh-perf-boost#readme",
40
- "bugs": {
41
- "url": "https://github.com/AlLHHH/dsh-perf-boost/issues"
42
- },
43
- "sideEffects": false,
44
- "dsh": {
45
- "bundle": {
46
- "patch": "./cordis.patch.yml"
47
- },
48
- "client": {
49
- "platform": "web",
50
- "inject": [
51
- "slots",
52
- "sessions"
53
- ]
54
- }
55
- }
56
- }
1
+ {
2
+ "name": "dsh-perf-boost",
3
+ "version": "0.2.2",
4
+ "description": "DSH 桌面端性能优化(纯客户端):长对话省渲染、窗口隐藏不排帧默认开,关背景模糊/减少动画/空闲降帧按需打开,设置页显示实测省下的主线程时间。Client-side rendering optimizations for DeepSeek Harness Desktop.",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "exports": {
8
+ ".": "./lib/index.js",
9
+ "./client": "./lib/client.js"
10
+ },
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "deepseek-harness",
14
+ "dsh",
15
+ "dsh-plugin",
16
+ "performance",
17
+ "fps",
18
+ "requestanimationframe",
19
+ "content-visibility",
20
+ "性能优化",
21
+ "性能",
22
+ "优化",
23
+ "增强",
24
+ "流畅",
25
+ "加速",
26
+ "卡顿",
27
+ "省内存",
28
+ "faster",
29
+ "smooth",
30
+ "speed-up"
31
+ ],
32
+ "files": [
33
+ "lib",
34
+ "assets",
35
+ "cordis.patch.yml",
36
+ "README.md",
37
+ "LICENSE",
38
+ "CHANGELOG.md"
39
+ ],
40
+ "engines": {
41
+ "dsh": ">=0.2.0-0"
42
+ },
43
+ "peerDependencies": {
44
+ "@deepseek-ai/cordis": "^4.0.1"
45
+ },
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/AlLHHH/dsh-perf-boost.git"
49
+ },
50
+ "homepage": "https://github.com/AlLHHH/dsh-perf-boost#readme",
51
+ "bugs": {
52
+ "url": "https://github.com/AlLHHH/dsh-perf-boost/issues"
53
+ },
54
+ "sideEffects": false,
55
+ "dsh": {
56
+ "bundle": {
57
+ "patch": "./cordis.patch.yml"
58
+ },
59
+ "client": {
60
+ "platform": "web",
61
+ "inject": [
62
+ "slots",
63
+ "sessions"
64
+ ]
65
+ }
66
+ }
67
+ }