@fffangchenghao/dsh-autoupdater 0.6.1 → 0.6.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 +18 -2
- package/main.js +119 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,8 @@ dsh plugin --profile web add github:fangchenghao/dsh-autoupdater
|
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
> 用 scoped 包名是为了不和上游 `dsh-autoupdater` 抢注同名包;bundle id 仍是 `dsh-autoupdater`,配置与状态路径不变。
|
|
34
|
+
>
|
|
35
|
+
> Windows 桌面版更推荐直接在 **插件管理器 → 安装插件** 里填包名安装;装完重启 App。
|
|
34
36
|
|
|
35
37
|
安装后重启 DSH(systemd 环境:`sudo systemctl restart <你的dsh服务名>`),然后在 **插件管理器 → 自动更新** 卡片配置。
|
|
36
38
|
|
|
@@ -87,11 +89,22 @@ dsh plugin --profile web add github:fangchenghao/dsh-autoupdater
|
|
|
87
89
|
|
|
88
90
|
因此桌面版的工作方式是:**静默把插件换版到磁盘 → 面板提示重启 → 你下次打开 App 时生效**。
|
|
89
91
|
|
|
92
|
+
### Windows 支持(0.6.2+)
|
|
93
|
+
|
|
94
|
+
0.6.2 起 Windows 桌面版与 macOS 使用同一条换版路径,并修掉了 0.6.1 里的几处 Unix-only 假设:
|
|
95
|
+
|
|
96
|
+
- **空闲判定**不再调用 `find -newermt`(Windows 的 `find.exe` 是文本搜索,不支持这些参数,会让插件永远认为“忙”),改为纯 Node 递归扫描 `%DSH_HOME%\sessions`。目录不存在 = 没有会话 = 空闲;只有根目录不可读才保守当忙。
|
|
97
|
+
- **`dsh.cmd` 调用**:PATH / sibling 找到的 `.cmd` 经 `cmd.exe` 执行并正确加引号;超时用 `taskkill /T /F` 结束整棵进程树,避免留下 pnpm 子进程。
|
|
98
|
+
- **环境变量**:没有 `HOME` 时用 `homedir()`(Windows 的 `USERPROFILE`);PATH 兼容 `Path` 键,并额外查找 `%APPDATA%\npm` / `%LOCALAPPDATA%\pnpm`。
|
|
99
|
+
- Windows 上不执行 DSH 本体更新(交给安装器/App 自带更新器),插件换版后同样只提示重启。
|
|
100
|
+
|
|
101
|
+
开发机是 macOS;Windows 分支是按 Node/Electron 的跨平台语义改的,并做了逻辑测试,但**尚未在真实 Windows 机器上端到端跑过**。如果 Windows 上仍失败,请把面板日志和 `%USERPROFILE%\.dsh\autoupdater\history.jsonl` 发到 GitHub issue。
|
|
102
|
+
|
|
90
103
|
## 维护说明
|
|
91
104
|
|
|
92
105
|
- 上游项目:[niubi6668889595/dsh-autoupdater](https://github.com/niubi6668889595/dsh-autoupdater)(MIT)。
|
|
93
|
-
- 本分支新增:桌面版 CLI 换版、npmmirror 优先、跳过 `link:`/git
|
|
94
|
-
- 发布渠道:npm `@fffangchenghao/dsh-autoupdater`,GitHub `fangchenghao/dsh-autoupdater`,tag `v0.6.
|
|
106
|
+
- 本分支新增:桌面版 CLI 换版、npmmirror 优先、跳过 `link:`/git 来源、只有真正升级成功才算“已应用”、Windows 空闲判定/.cmd 调用适配;完整差异见 git log。
|
|
107
|
+
- 发布渠道:npm `@fffangchenghao/dsh-autoupdater`,GitHub `fangchenghao/dsh-autoupdater`,tag `v0.6.2`。
|
|
95
108
|
- 与上游同步:把上游加为 remote,rebase/merge 到本分支后重新发版即可。
|
|
96
109
|
|
|
97
110
|
## 安全模型
|
|
@@ -106,6 +119,7 @@ dsh plugin --profile web add github:fangchenghao/dsh-autoupdater
|
|
|
106
119
|
- 插件更新只覆盖声明 `dsh.bundle` 的 npm 形态插件;GitHub 源码安装的插件版本比对不适用
|
|
107
120
|
- 本体更新依赖 npm 全局安装形态(git checkout 形态请用 [dsh-updater](https://github.com/GodCC6/dsh-updater);桌面 App 不参与本体更新,由 App 自带更新器负责)
|
|
108
121
|
- 空闲判定基于本机 `$DSH_HOME/sessions` 的文件 mtime,远程会话(SSH 场景)不计入
|
|
122
|
+
- Windows 原生环境尚未实机验证(0.6.2 已移除 Unix-only 依赖,仍建议先看面板日志确认 desktop CLI 路径)
|
|
109
123
|
|
|
110
124
|
## 致谢
|
|
111
125
|
|
|
@@ -133,4 +147,6 @@ MIT
|
|
|
133
147
|
|
|
134
148
|
Install: `dsh plugin --profile web add @fffangchenghao/dsh-autoupdater` (or `github:fangchenghao/dsh-autoupdater`), then restart DSH and configure under **Plugin Manager → dsh-autoupdater**.
|
|
135
149
|
|
|
150
|
+
**Windows (0.6.2+)**: the desktop path is shared with macOS. 0.6.2 removes the Unix `find` idle check, invokes `dsh.cmd` through `cmd.exe`, falls back to `homedir()` when `HOME` is absent, and leaves DSH core updates to the installer/app. Windows has not been tested end-to-end on a real machine yet; logs are welcome in GitHub issues.
|
|
151
|
+
|
|
136
152
|
See the Chinese sections above for full details — the configuration table and idle-detection notes apply to both languages.
|
package/main.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { readFile, writeFile, mkdir, appendFile } from 'node:fs/promises';
|
|
2
2
|
import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
|
|
3
|
-
import {
|
|
3
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
4
4
|
import { basename, delimiter, dirname, join } from 'node:path';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
5
6
|
|
|
6
7
|
/** 本插件的真实包名(上游名或发布用的 scoped 名),用于把自己排除在自动更新之外。 */
|
|
7
8
|
const SELF_NAME = (() => {
|
|
@@ -139,6 +140,48 @@ async function readJson(path) {
|
|
|
139
140
|
try { return JSON.parse(await readFile(path, 'utf8')); } catch { return null; }
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
/**
|
|
144
|
+
* 递归找出目录里最新的文件 mtime(毫秒)。
|
|
145
|
+
* 目录不存在 → 0(没有会话 = 空闲);根目录读不了 → 抛错(调用方保守当忙)。
|
|
146
|
+
* 用纯 Node 实现,避免 Windows 上没有 Unix `find` 导致空闲判定永远为忙。
|
|
147
|
+
*/
|
|
148
|
+
function newestFileMtimeMs(dir) {
|
|
149
|
+
if (!existsSync(dir)) return 0;
|
|
150
|
+
let newest = 0;
|
|
151
|
+
const stack = [dir];
|
|
152
|
+
while (stack.length > 0) {
|
|
153
|
+
const current = stack.pop();
|
|
154
|
+
let entries;
|
|
155
|
+
try {
|
|
156
|
+
entries = readdirSync(current, { withFileTypes: true });
|
|
157
|
+
} catch (e) {
|
|
158
|
+
if (current === dir) throw e;
|
|
159
|
+
continue; // 子目录权限不足不应让整个探测失败
|
|
160
|
+
}
|
|
161
|
+
for (const entry of entries) {
|
|
162
|
+
const full = join(current, entry.name);
|
|
163
|
+
if (entry.isDirectory()) { stack.push(full); continue; }
|
|
164
|
+
if (!entry.isFile()) continue;
|
|
165
|
+
try {
|
|
166
|
+
const m = statSync(full).mtimeMs;
|
|
167
|
+
if (m > newest) newest = m;
|
|
168
|
+
} catch {}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return newest;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Windows 的 npm 全局命令是 dsh.cmd,不能直接 spawn(必须经 cmd.exe)。
|
|
176
|
+
* 返回已加引号的命令行;其他平台或 .exe 返回 null。
|
|
177
|
+
* 平台参数只用于测试,生产调用走默认值。
|
|
178
|
+
*/
|
|
179
|
+
function windowsShellCommand(command, args, platform = process.platform) {
|
|
180
|
+
if (platform !== 'win32' || !/\.(cmd|bat)$/i.test(command)) return null;
|
|
181
|
+
const quote = (value) => `"${String(value).replace(/"/g, '""')}"`;
|
|
182
|
+
return [quote(command), ...args.map(quote)].join(' ');
|
|
183
|
+
}
|
|
184
|
+
|
|
142
185
|
/** 运行子进程并收集 stdout+stderr;超时或无法启动 → code -1。 */
|
|
143
186
|
function spawnCapture(command, args, { env = process.env, timeoutMs = 300000 } = {}) {
|
|
144
187
|
return new Promise((resolve) => {
|
|
@@ -150,15 +193,30 @@ function spawnCapture(command, args, { env = process.env, timeoutMs = 300000 } =
|
|
|
150
193
|
settled = true;
|
|
151
194
|
resolve({ code, output });
|
|
152
195
|
};
|
|
196
|
+
const shellCommand = windowsShellCommand(command, args);
|
|
153
197
|
try {
|
|
154
|
-
child =
|
|
198
|
+
child = shellCommand
|
|
199
|
+
? spawn(shellCommand, [], { stdio: ['ignore', 'pipe', 'pipe'], env, shell: true, windowsHide: true })
|
|
200
|
+
: spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'], env, windowsHide: true });
|
|
155
201
|
} catch (e) {
|
|
156
202
|
output += String(e?.message ?? e);
|
|
157
203
|
finish(-1);
|
|
158
204
|
return;
|
|
159
205
|
}
|
|
206
|
+
const killTree = () => {
|
|
207
|
+
if (!child?.pid) return;
|
|
208
|
+
if (process.platform === 'win32') {
|
|
209
|
+
try {
|
|
210
|
+
execFileSync('taskkill', ['/pid', String(child.pid), '/T', '/F'], {
|
|
211
|
+
stdio: 'ignore', windowsHide: true,
|
|
212
|
+
});
|
|
213
|
+
} catch {}
|
|
214
|
+
} else {
|
|
215
|
+
try { child.kill('SIGKILL'); } catch {}
|
|
216
|
+
}
|
|
217
|
+
};
|
|
160
218
|
const timer = setTimeout(() => {
|
|
161
|
-
|
|
219
|
+
killTree();
|
|
162
220
|
output += `\n[dsh-autoupdater] timeout after ${timeoutMs}ms`;
|
|
163
221
|
finish(-1);
|
|
164
222
|
}, timeoutMs);
|
|
@@ -185,12 +243,13 @@ function lastMeaningfulLine(text, limit = 220) {
|
|
|
185
243
|
export const inject = ['agents', 'pluginManager', 'webServer'];
|
|
186
244
|
|
|
187
245
|
export function apply(ctx, rawConfig = {}) {
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
const
|
|
246
|
+
// Windows 上通常没有 HOME,只有 USERPROFILE;homedir() 会选对。
|
|
247
|
+
const home = process.env.DSH_HOME || join(homedir(), '.dsh');
|
|
248
|
+
const stateDir = join(home, 'autoupdater');
|
|
249
|
+
const statePath = join(stateDir, 'status.json');
|
|
250
|
+
const historyPath = join(stateDir, 'history.jsonl');
|
|
251
|
+
const pendingPath = join(stateDir, 'pending-update.json');
|
|
252
|
+
const hotConfigPath = join(stateDir, 'config.json');
|
|
194
253
|
|
|
195
254
|
let state = { lastRun: null, lastNotice: null, updates: [], lastError: null };
|
|
196
255
|
readJson(statePath).then((s) => { if (s) state = { ...state, ...s }; }).catch(() => {});
|
|
@@ -208,15 +267,15 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
208
267
|
// 多个时取 node_modules 最近修改的。找不到 = 磁盘回读退化为仅信安装结果。
|
|
209
268
|
let profileDir = process.env.DSH_PROFILE_DIR || null;
|
|
210
269
|
try {
|
|
211
|
-
const base =
|
|
270
|
+
const base = join(home, 'profiles');
|
|
212
271
|
const candidates = readdirSync(base)
|
|
213
|
-
.map((name) =>
|
|
214
|
-
.filter((dir) => existsSync(
|
|
272
|
+
.map((name) => join(base, name))
|
|
273
|
+
.filter((dir) => existsSync(join(dir, 'node_modules')));
|
|
215
274
|
if (candidates.length === 1) {
|
|
216
275
|
profileDir = candidates[0];
|
|
217
276
|
} else if (candidates.length > 1) {
|
|
218
277
|
profileDir = candidates
|
|
219
|
-
.map((dir) => ({ dir, m: statSync(
|
|
278
|
+
.map((dir) => ({ dir, m: statSync(join(dir, 'node_modules')).mtimeMs }))
|
|
220
279
|
.sort((a, b) => b.m - a.m)[0].dir;
|
|
221
280
|
}
|
|
222
281
|
if (profileDir && config.verbose) log('profileDir detected:', profileDir);
|
|
@@ -237,9 +296,15 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
237
296
|
*/
|
|
238
297
|
function resolvePluginCli() {
|
|
239
298
|
if (isDesktopApp() && process.resourcesPath) {
|
|
240
|
-
const
|
|
241
|
-
'
|
|
242
|
-
|
|
299
|
+
const desktopCliCandidates = [
|
|
300
|
+
join(process.resourcesPath, 'app.asar', 'dsh', 'node_modules',
|
|
301
|
+
'@deepseek-ai', 'dsh-desktop-host', 'lib', 'cli.js'),
|
|
302
|
+
// 未打包/开发模式:resources/app/dsh/...
|
|
303
|
+
join(process.resourcesPath, 'app', 'dsh', 'node_modules',
|
|
304
|
+
'@deepseek-ai', 'dsh-desktop-host', 'lib', 'cli.js'),
|
|
305
|
+
];
|
|
306
|
+
const desktopCli = desktopCliCandidates.find((candidate) => existsSync(candidate));
|
|
307
|
+
if (desktopCli) {
|
|
243
308
|
return {
|
|
244
309
|
command: process.execPath,
|
|
245
310
|
prefixArgs: [desktopCli],
|
|
@@ -247,15 +312,21 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
247
312
|
label: 'desktop-cli',
|
|
248
313
|
};
|
|
249
314
|
}
|
|
250
|
-
log('desktop CLI not found at',
|
|
315
|
+
log('desktop CLI not found at', desktopCliCandidates[0]);
|
|
251
316
|
}
|
|
252
317
|
// 普通 npm 全局安装:dsh 与 node 同目录(Linux 常见布局)
|
|
253
|
-
const
|
|
318
|
+
const binName = process.platform === 'win32' ? 'dsh.cmd' : 'dsh';
|
|
319
|
+
const sibling = join(dirname(process.execPath), binName);
|
|
254
320
|
if (existsSync(sibling)) return { command: sibling, prefixArgs: [], env: {}, label: 'sibling-cli' };
|
|
255
321
|
// 最后退回 PATH(macOS 上 npm 全局安装常见于 ~/.local/bin,而非 node 同目录)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
322
|
+
// Windows 环境变量大小写不敏感,但键名通常是 Path 而不是 PATH。
|
|
323
|
+
const pathEnv = process.env.Path ?? process.env.PATH ?? '';
|
|
324
|
+
const pathDirs = String(pathEnv).split(delimiter).filter(Boolean);
|
|
325
|
+
if (process.platform === 'win32') {
|
|
326
|
+
if (process.env.APPDATA) pathDirs.push(join(process.env.APPDATA, 'npm'));
|
|
327
|
+
if (process.env.LOCALAPPDATA) pathDirs.push(join(process.env.LOCALAPPDATA, 'pnpm'));
|
|
328
|
+
}
|
|
329
|
+
for (const dir of pathDirs) {
|
|
259
330
|
const candidate = join(dir, binName);
|
|
260
331
|
if (existsSync(candidate)) return { command: candidate, prefixArgs: [], env: {}, label: 'path-cli' };
|
|
261
332
|
}
|
|
@@ -277,20 +348,18 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
277
348
|
// 借鉴 dsh-self-upgrade 的判定(实测比 agent.status 可靠):
|
|
278
349
|
// 会话日志 2 分钟内无任何写入 = 无进行中的工作。agent.status 存在 turn 结束
|
|
279
350
|
// 后仍停留 'running' 的情况(本机实测),会永久阻塞空闲判定,故弃用。
|
|
280
|
-
const sessionsDir =
|
|
351
|
+
const sessionsDir = join(home, 'sessions');
|
|
281
352
|
let sessionsBusyCache = { at: 0, busy: true };
|
|
282
353
|
const anyAgentRunning = () => {
|
|
283
354
|
const now = Date.now();
|
|
284
355
|
if (now - sessionsBusyCache.at < 8000) return sessionsBusyCache.busy; // 8s 缓存
|
|
285
356
|
try {
|
|
286
|
-
//
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
sessionsBusyCache = { at: now, busy: out !== '' };
|
|
293
|
-
} catch { sessionsBusyCache = { at: now, busy: true }; } // 探测失败保守当忙
|
|
357
|
+
// 2 分钟内有文件写入 = 有进行中的工作;目录不存在 = 没有会话 = 空闲。
|
|
358
|
+
const newest = newestFileMtimeMs(sessionsDir);
|
|
359
|
+
sessionsBusyCache = { at: now, busy: newest > now - 120_000 };
|
|
360
|
+
} catch {
|
|
361
|
+
sessionsBusyCache = { at: now, busy: true }; // 根目录探测失败保守当忙
|
|
362
|
+
}
|
|
294
363
|
return sessionsBusyCache.busy;
|
|
295
364
|
};
|
|
296
365
|
|
|
@@ -411,7 +480,9 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
411
480
|
log('CLI add:', reason);
|
|
412
481
|
return { ok: false, code: -1, output: '', reason };
|
|
413
482
|
}
|
|
414
|
-
const profileName = profileDir
|
|
483
|
+
const profileName = profileDir
|
|
484
|
+
? basename(profileDir)
|
|
485
|
+
: (process.env.DSH_PROFILE || (isDesktopApp() ? 'desktop' : 'web'));
|
|
415
486
|
// pnpm 自身不会在 registry 之间回退。国内链路 registry.npmjs.org 实测会连续
|
|
416
487
|
// CONNECT_TIMEOUT(一次 130 个包安装用了 3 分 14 秒),而版本查询用的 npmmirror
|
|
417
488
|
// 同一版本有同步副本、1~3 秒装完。先问 npmmirror,失败再退回 pnpm 默认源。
|
|
@@ -487,6 +558,7 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
487
558
|
|
|
488
559
|
/** 自动探测管理本进程的 systemd 服务名(无则返回空 = 裸进程模式) */
|
|
489
560
|
function detectSystemdUnit() {
|
|
561
|
+
if (process.platform !== 'linux') return '';
|
|
490
562
|
try {
|
|
491
563
|
const cgroup = readFileSync('/proc/self/cgroup', 'utf8');
|
|
492
564
|
const m = cgroup.match(/\/system\.slice\/([a-z0-9@._-]+\.service)/i);
|
|
@@ -524,7 +596,6 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
524
596
|
*/
|
|
525
597
|
async function applyHarnessUpdateNow() {
|
|
526
598
|
const pendingExists = existsSync(pendingPath);
|
|
527
|
-
const unit = config.systemdUnit || detectSystemdUnit();
|
|
528
599
|
|
|
529
600
|
// 桌面 App:DSH 本体由 electron-updater 负责,npm -g 不能也不该覆盖 .app 内的运行时;
|
|
530
601
|
// 插件更新也不能从进程内安全 relaunch(会连带杀掉正在跑的会话)。统一标记“待重启”。
|
|
@@ -534,6 +605,15 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
534
605
|
return;
|
|
535
606
|
}
|
|
536
607
|
|
|
608
|
+
// Windows 非桌面模式:没有 systemd,也不支持 apply-update.mjs 的 POSIX 重启方式。
|
|
609
|
+
if (process.platform === 'win32') {
|
|
610
|
+
log('Windows: in-process restart/apply unsupported — restart required');
|
|
611
|
+
await appendHistory({ kind: 'restart-required', reason: 'win32', pending: pendingExists });
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const unit = config.systemdUnit || detectSystemdUnit();
|
|
616
|
+
|
|
537
617
|
// 路径 A:仅插件更新(无 pending 文件)→ 无需 npm install,直接重启激活新插件
|
|
538
618
|
if (!pendingExists) {
|
|
539
619
|
log('no pending harness update — plugin-only restart');
|
|
@@ -616,6 +696,11 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
616
696
|
if (config.verbose) log('desktop build: DSH core updates are owned by the app updater — skip harness check');
|
|
617
697
|
return;
|
|
618
698
|
}
|
|
699
|
+
if (process.platform === 'win32') {
|
|
700
|
+
// Windows 的 npm 全局布局与 POSIX 不同,且没有 systemd 重启路径;本体交给安装器/App 更新。
|
|
701
|
+
if (config.verbose) log('Windows: DSH core updates are handled by the installer/app updater — skip harness check');
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
619
704
|
if (existsSync(pendingPath)) {
|
|
620
705
|
if (config.verbose) log('pending update already staged, skip harness check');
|
|
621
706
|
return;
|
|
@@ -654,7 +739,7 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
654
739
|
/** 扫描 profile 的 cordis.patch.yml,列出 !!js ctx.<service> 这类跨版本易失效的引用 */
|
|
655
740
|
function scanPatchRisks() {
|
|
656
741
|
try {
|
|
657
|
-
const patchPath =
|
|
742
|
+
const patchPath = join(profileDir || join(home, 'profiles', 'web'), 'cordis.patch.yml');
|
|
658
743
|
if (!existsSync(patchPath)) return [];
|
|
659
744
|
const text = readFileSync(patchPath, 'utf8');
|
|
660
745
|
const found = new Set();
|
|
@@ -752,7 +837,7 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
752
837
|
// 装完回读磁盘真实版本验证,不再信返回值
|
|
753
838
|
let diskVer = null;
|
|
754
839
|
if (profileDir) {
|
|
755
|
-
const diskPkg = await readJson(
|
|
840
|
+
const diskPkg = await readJson(join(profileDir, 'node_modules', b.name, 'package.json')).catch(() => null);
|
|
756
841
|
diskVer = diskPkg?.version ?? null;
|
|
757
842
|
}
|
|
758
843
|
const reallyUpdated = diskVer == null ? true : diskVer !== b.version; // 无 profileDir 时退化为信安装结果
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fffangchenghao/dsh-autoupdater",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Maintained fork of dsh-autoupdater with macOS/Windows desktop fixes. Scheduled update checks for DSH itself and installed plugins, applied only in idle windows.",
|