@jiugeqqqqq/dsh-house 0.2.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/README.md +16 -0
- package/lib/client.js +7 -0
- package/lib/host/index.js +35 -1
- package/lib/update-check.js +116 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -102,6 +102,22 @@ dsh plugin --profile web add @jiugeqqqqq/dsh-house
|
|
|
102
102
|
|
|
103
103
|
默认就是空的,正常不用动。
|
|
104
104
|
|
|
105
|
+
## 更新
|
|
106
|
+
|
|
107
|
+
```powershell
|
|
108
|
+
dsh plugin --profile desktop add @jiugeqqqqq/dsh-house@latest
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
重启 DSH 生效。
|
|
112
|
+
|
|
113
|
+
**自动检测**:插件每天查一次 npm(配置项 `updateCheck`,默认开)。有新版本时:
|
|
114
|
+
|
|
115
|
+
- `<DSH_HOME>/plugin-updates.json` 里记一条
|
|
116
|
+
- 日志里写一行
|
|
117
|
+
- 侧边栏「房子」页标题旁显示 `v0.2.1 → 0.3.0 可更新`
|
|
118
|
+
|
|
119
|
+
**它不会自动安装** —— DSH 没有热重载,装了也要重启;运行时替换自己的代码风险不值得。
|
|
120
|
+
|
|
105
121
|
## 常见问题
|
|
106
122
|
|
|
107
123
|
**Q:页面空白 / 显示"解析不出条目"?**
|
package/lib/client.js
CHANGED
|
@@ -227,6 +227,13 @@ window.__ModuleLoader__.load({
|
|
|
227
227
|
React.createElement('h1', { style: styles.title }, '房子'),
|
|
228
228
|
React.createElement('span', { style: styles.sub }, '常驻的资产与能力清单'),
|
|
229
229
|
React.createElement('span', { style: styles.note }, '共 ' + String(total) + ' 条'),
|
|
230
|
+
s.data && s.data.version
|
|
231
|
+
? React.createElement(
|
|
232
|
+
'span',
|
|
233
|
+
{ style: Object.assign({}, styles.note, s.data.updateAvailable ? { color: '#e0a030' } : null) },
|
|
234
|
+
'v' + s.data.version + (s.data.updateAvailable ? ' → ' + s.data.latest + ' 可更新' : ''),
|
|
235
|
+
)
|
|
236
|
+
: null,
|
|
230
237
|
),
|
|
231
238
|
);
|
|
232
239
|
|
package/lib/host/index.js
CHANGED
|
@@ -17,10 +17,19 @@ import { readFile, writeFile, mkdir, copyFile } from 'node:fs/promises';
|
|
|
17
17
|
import fs from 'node:fs';
|
|
18
18
|
import zlib from 'node:zlib';
|
|
19
19
|
import path from 'node:path';
|
|
20
|
+
import { createUpdateChecker } from '../update-check.js';
|
|
20
21
|
|
|
21
22
|
export const name = 'house';
|
|
22
23
|
export const inject = [];
|
|
23
24
|
|
|
25
|
+
const pkgVersion = (() => {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version || '0.0.0';
|
|
28
|
+
} catch {
|
|
29
|
+
return '0.0.0';
|
|
30
|
+
}
|
|
31
|
+
})();
|
|
32
|
+
|
|
24
33
|
const ROUTE_PREFIX = '/dsh-house/api';
|
|
25
34
|
|
|
26
35
|
function writeJson(response, status, payload) {
|
|
@@ -102,6 +111,19 @@ export function apply(ctx, config = {}) {
|
|
|
102
111
|
? config.sessionsDir
|
|
103
112
|
: path.join(dshHome, 'sessions');
|
|
104
113
|
|
|
114
|
+
// 版本自检:只查 npm 有没有新版,不自动安装。
|
|
115
|
+
// 注意:本地开发目录的 package.json name 是 dsh-house,而 npm 上是 scoped 包,所以写死 npmName。
|
|
116
|
+
const updateChecker = createUpdateChecker({
|
|
117
|
+
npmName: '@jiugeqqqqq/dsh-house',
|
|
118
|
+
version: pkgVersion,
|
|
119
|
+
log: (m) => ctx.logger?.info('dsh-house: ' + m),
|
|
120
|
+
enabled: config.updateCheck !== false,
|
|
121
|
+
});
|
|
122
|
+
const updateTimer = setTimeout(() => {
|
|
123
|
+
updateChecker.check().catch(() => {});
|
|
124
|
+
}, 15000);
|
|
125
|
+
updateTimer.unref?.();
|
|
126
|
+
|
|
105
127
|
// 房子清单不存在时自动建一份模板:别人装完第一次打开页面就有东西可看,
|
|
106
128
|
// 不会像 2026-10-26 那样提示「读不到房子清单」。
|
|
107
129
|
const TEMPLATE = [
|
|
@@ -425,7 +447,19 @@ export function apply(ctx, config = {}) {
|
|
|
425
447
|
|
|
426
448
|
try {
|
|
427
449
|
if (method === 'list') {
|
|
428
|
-
|
|
450
|
+
const result = await loadAssets();
|
|
451
|
+
if (result.ok && result.data) {
|
|
452
|
+
const u = updateChecker.get() || {};
|
|
453
|
+
result.data.version = pkgVersion;
|
|
454
|
+
result.data.latest = u.latest || '';
|
|
455
|
+
result.data.updateAvailable = u.updateAvailable === true;
|
|
456
|
+
}
|
|
457
|
+
writeJson(response, 200, result);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (method === 'checkUpdate') {
|
|
461
|
+
const rec = await updateChecker.check({ force: true });
|
|
462
|
+
writeJson(response, 200, { ok: true, data: { ...(rec || {}), version: pkgVersion } });
|
|
429
463
|
return;
|
|
430
464
|
}
|
|
431
465
|
if (method === 'read') {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// dsh-affection —— 版本更新检查(自动检测 npm 上有没有新版本)
|
|
2
|
+
//
|
|
3
|
+
// 只做「检测 + 报告」,不擅自替换自己的代码:DSH 没有热重载,
|
|
4
|
+
// 插件在运行中改自己的文件也只会下次重启才生效,风险不值得。
|
|
5
|
+
//
|
|
6
|
+
// 结果写到 <DSH_HOME>/plugin-updates.json,页面/日志都能读:
|
|
7
|
+
// { "dsh-affection": { current, latest, updateAvailable, checkedAt, lastError? } }
|
|
8
|
+
//
|
|
9
|
+
// 频率:默认每 24 小时查一次;失败也记录时间,避免离线时每步重试。
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import os from 'node:os';
|
|
13
|
+
|
|
14
|
+
/** '0.10.2' vs '0.9.9' → 1;相等 0;更小 -1。忽略预发布后缀。 */
|
|
15
|
+
export function compareVersions(a, b) {
|
|
16
|
+
const parse = (v) =>
|
|
17
|
+
String(v || '')
|
|
18
|
+
.trim()
|
|
19
|
+
.replace(/^v/i, '')
|
|
20
|
+
.split('-')[0]
|
|
21
|
+
.split('.')
|
|
22
|
+
.map((x) => Number.parseInt(x, 10) || 0);
|
|
23
|
+
const pa = parse(a);
|
|
24
|
+
const pb = parse(b);
|
|
25
|
+
const len = Math.max(pa.length, pb.length);
|
|
26
|
+
for (let i = 0; i < len; i++) {
|
|
27
|
+
const x = pa[i] || 0;
|
|
28
|
+
const y = pb[i] || 0;
|
|
29
|
+
if (x > y) return 1;
|
|
30
|
+
if (x < y) return -1;
|
|
31
|
+
}
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function createUpdateChecker({
|
|
36
|
+
npmName,
|
|
37
|
+
version,
|
|
38
|
+
log,
|
|
39
|
+
enabled = true,
|
|
40
|
+
intervalHours = 24,
|
|
41
|
+
registry = 'https://registry.npmjs.org',
|
|
42
|
+
}) {
|
|
43
|
+
const dshHome = process.env.DSH_HOME || path.join(os.homedir(), '.dsh');
|
|
44
|
+
const statePath = path.join(dshHome, 'plugin-updates.json');
|
|
45
|
+
|
|
46
|
+
function readState() {
|
|
47
|
+
try {
|
|
48
|
+
const j = JSON.parse(fs.readFileSync(statePath, 'utf8'));
|
|
49
|
+
return j && typeof j === 'object' ? j : {};
|
|
50
|
+
} catch {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function writeState(state) {
|
|
56
|
+
try {
|
|
57
|
+
fs.mkdirSync(path.dirname(statePath), { recursive: true });
|
|
58
|
+
const tmp = `${statePath}.tmp-${process.pid}`;
|
|
59
|
+
fs.writeFileSync(tmp, JSON.stringify(state, null, 1), 'utf8');
|
|
60
|
+
fs.renameSync(tmp, statePath);
|
|
61
|
+
} catch {}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function record(partial) {
|
|
65
|
+
const state = readState();
|
|
66
|
+
state[npmName] = { ...(state[npmName] || {}), ...partial };
|
|
67
|
+
writeState(state);
|
|
68
|
+
return state[npmName];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function check({ force = false } = {}) {
|
|
72
|
+
if (!enabled) return null;
|
|
73
|
+
const prev = readState()[npmName] || {};
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
const interval = Math.max(1, Number(intervalHours) || 24) * 3600 * 1000;
|
|
76
|
+
if (!force && prev.checkedAt && now - prev.checkedAt < interval) return prev;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const res = await fetch(`${registry.replace(/\/+$/, '')}/${npmName.replace('/', '%2F')}/latest`, {
|
|
80
|
+
headers: { accept: 'application/json' },
|
|
81
|
+
signal: AbortSignal.timeout(8000),
|
|
82
|
+
});
|
|
83
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
84
|
+
const j = await res.json();
|
|
85
|
+
const latest = String(j?.version || '');
|
|
86
|
+
if (!latest) throw new Error('响应里没有 version');
|
|
87
|
+
const available = compareVersions(latest, version) > 0;
|
|
88
|
+
const rec = record({
|
|
89
|
+
current: version,
|
|
90
|
+
latest,
|
|
91
|
+
updateAvailable: available,
|
|
92
|
+
checkedAt: now,
|
|
93
|
+
lastError: '',
|
|
94
|
+
updateCommand: available ? `dsh plugin --profile <你的profile> add ${npmName}@latest` : '',
|
|
95
|
+
});
|
|
96
|
+
if (available) {
|
|
97
|
+
log?.(`发现新版本 ${latest}(当前 ${version}):dsh plugin --profile <你的profile> add ${npmName}@latest`);
|
|
98
|
+
}
|
|
99
|
+
return rec;
|
|
100
|
+
} catch (e) {
|
|
101
|
+
const rec = record({
|
|
102
|
+
current: version,
|
|
103
|
+
checkedAt: now,
|
|
104
|
+
lastError: String(e?.message ?? e),
|
|
105
|
+
});
|
|
106
|
+
log?.(`检查更新失败(不影响使用):${rec.lastError}`);
|
|
107
|
+
return rec;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function get() {
|
|
112
|
+
return readState()[npmName] || null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { check, get, npmName, version };
|
|
116
|
+
}
|
package/package.json
CHANGED