dsh-clean-desktop-shell 0.1.1 → 0.1.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.en.md +79 -2
- package/README.md +73 -2
- package/electron/config.js +0 -2
- package/electron/main.js +14 -14
- package/electron/tray.js +7 -1
- package/electron/update.js +133 -6
- package/package.json +112 -104
- package/version.txt +1 -1
package/README.en.md
CHANGED
|
@@ -12,6 +12,7 @@ Does exactly one thing: wraps your already-configured DSH Web in a clean native
|
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
[](https://github.com/Icather/dsh-clean-desktop-shell/releases/latest)
|
|
14
14
|
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
15
|
+
[](https://github.com/Icather/dsh-clean-desktop-shell/graphs/contributors)
|
|
15
16
|
|
|
16
17
|
</div>
|
|
17
18
|
|
|
@@ -50,13 +51,22 @@ Key differences from other desktop clients in the ecosystem:
|
|
|
50
51
|
|
|
51
52
|
| Platform | Shell | Status |
|
|
52
53
|
|:--|:--|:--|
|
|
53
|
-
| Windows | ✅ Electron (frameless + native window buttons) |
|
|
54
|
-
| macOS | ✅ Electron (hiddenInset) |
|
|
54
|
+
| Windows | ✅ Electron (frameless + native window buttons) | Released (NSIS installer) |
|
|
55
|
+
| macOS | ✅ Electron (hiddenInset) | Released (CI builds Intel + Apple Silicon DMG) |
|
|
55
56
|
| Linux | — (browser / PWA to the core) | Not planned |
|
|
56
57
|
| Termux / phone / tablet | — (headless / PWA to the core) | Covered by remote core access |
|
|
57
58
|
|
|
58
59
|
## Install
|
|
59
60
|
|
|
61
|
+
**Option 1: download the installer from Releases (recommended for end users)**
|
|
62
|
+
|
|
63
|
+
- Windows: `DSH-Clean-Desktop-Shell-Setup-<version>.exe`
|
|
64
|
+
- macOS: `DSH-Clean-Desktop-Shell-<version>.dmg` (Intel) or `-arm64.dmg` (Apple Silicon)
|
|
65
|
+
|
|
66
|
+
The first time you run the Windows installer you may see a SmartScreen warning — **this is normal for unsigned programs, not a virus**, see "Windows SmartScreen warning" below.
|
|
67
|
+
|
|
68
|
+
**Option 2: install as a DSH plugin (developers)**
|
|
69
|
+
|
|
60
70
|
```sh
|
|
61
71
|
dsh plugin --profile web add dsh-clean-desktop-shell
|
|
62
72
|
```
|
|
@@ -65,12 +75,63 @@ Restart `dsh web`, then launch the shell from the tray / shortcut.
|
|
|
65
75
|
|
|
66
76
|
> The shell needs a reachable `dsh web` service (local or configured remote address). See Usage.
|
|
67
77
|
|
|
78
|
+
### Windows SmartScreen warning
|
|
79
|
+
|
|
80
|
+
**Why does the warning appear?**
|
|
81
|
+
|
|
82
|
+
Our installer has **no code signing certificate** (a personal open-source project — certificates cost a few hundred USD per year). Microsoft Defender SmartScreen is a **reputation system**: it decides whether a program is trusted based on download volume plus a history of clean executions. For a rarely-downloaded, unsigned `.exe` it cannot confirm reputation, so it warns. **This does not mean the file is a virus**: the project is fully open source and the binaries are built by GitHub Actions from this repository (see `.github/workflows/build.yml`).
|
|
83
|
+
|
|
84
|
+
**When Edge downloads the file:**
|
|
85
|
+
|
|
86
|
+
It may be flagged as "not commonly downloaded". To keep it:
|
|
87
|
+
|
|
88
|
+
1. Hover the download entry and click the `...` menu on the right
|
|
89
|
+
2. Choose **Keep**
|
|
90
|
+
3. Confirm with **Keep anyway**
|
|
91
|
+
|
|
92
|
+
**When you double-click the installer:**
|
|
93
|
+
|
|
94
|
+
A blue dialog appears: "Windows protected your PC" — Microsoft Defender SmartScreen prevented an unrecognized app from starting.
|
|
95
|
+
|
|
96
|
+
1. Click **More info**
|
|
97
|
+
2. Verify the file name is `DSH-Clean-Desktop-Shell-Setup-<version>.exe`
|
|
98
|
+
3. Click **Run anyway**
|
|
99
|
+
|
|
100
|
+
**Alternative: unblock the file once (recommended)**
|
|
101
|
+
|
|
102
|
+
Right-click the installer → Properties → General → tick **Unblock** at the bottom → OK. No more warnings afterwards.
|
|
103
|
+
|
|
104
|
+
Or bulk-unblock via PowerShell:
|
|
105
|
+
|
|
106
|
+
```powershell
|
|
107
|
+
Unblock-File -Path "$env:USERPROFILE\Downloads\DSH-Clean-Desktop-Shell-Setup-*.exe"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
> A code signing certificate (EV or Azure Trusted Signing) would remove this warning entirely, but it costs money and is rarely worth it for individual open-source maintainers. We may adopt signing when the project allows.
|
|
111
|
+
|
|
68
112
|
## Usage
|
|
69
113
|
|
|
70
114
|
1. Start `dsh web` (or configure a remote service address).
|
|
71
115
|
2. Launch the shell: it auto-detects local 3080; if not running it starts the service per configuration.
|
|
72
116
|
3. Drag the window by its top area (right side reserved for native buttons); close minimizes to tray by default.
|
|
73
117
|
|
|
118
|
+
**All backend controls live in the tray** — the main window stays a pure shell:
|
|
119
|
+
|
|
120
|
+
- Start / restart / stop the backend (with progress dialogs; stopping really
|
|
121
|
+
shuts down the service on 3080, including externally started instances)
|
|
122
|
+
- Auto-detect backend · set the backend install folder (auto-detect default)
|
|
123
|
+
- Reload window · check for updates · repo homepage · auto-launch
|
|
124
|
+
|
|
125
|
+
**Window reliability (Edge-style instant refresh):**
|
|
126
|
+
|
|
127
|
+
- Shows immediately on launch, never waits for the backend
|
|
128
|
+
- While the backend is down, a local "backend offline" screen is shown and
|
|
129
|
+
re-probed; the real page loads automatically the moment it answers
|
|
130
|
+
- The instant the backend stops (tray stop, kill or crash) the window flips
|
|
131
|
+
back to the offline screen — a stale page never fakes "still alive"
|
|
132
|
+
- The offline screen has self-service buttons: reload / start backend /
|
|
133
|
+
auto-detect backend / set backend install folder
|
|
134
|
+
|
|
74
135
|
## Development
|
|
75
136
|
|
|
76
137
|
```sh
|
|
@@ -82,9 +143,25 @@ npm run pack # package NSIS (Win) / DMG (mac)
|
|
|
82
143
|
|
|
83
144
|
## Changelog
|
|
84
145
|
|
|
146
|
+
### 0.1.1
|
|
147
|
+
- Backend lifecycle: fixed `spawn EINVAL` / stuck "starting" on Windows;
|
|
148
|
+
"stop backend" now really shuts the service down (including externally
|
|
149
|
+
started instances); start/restart/stop show progress dialogs.
|
|
150
|
+
- Window reliability: shows instantly on double-click; flips to the offline
|
|
151
|
+
screen the moment the backend stops; auto-reconnects when it comes back
|
|
152
|
+
(Edge-style instant refresh).
|
|
153
|
+
- Offline screen self-service: reload / start backend / auto-detect backend /
|
|
154
|
+
set backend install folder.
|
|
155
|
+
- Tray: new "reload window" item; macOS builds released (Intel + Apple
|
|
156
|
+
Silicon DMG).
|
|
157
|
+
|
|
85
158
|
### 0.1.0
|
|
86
159
|
- Initial release: Electron shell skeleton, system tray / single instance / auto-launch, DSH plugin mounting.
|
|
87
160
|
|
|
161
|
+
## Contributing
|
|
162
|
+
|
|
163
|
+
Contributions of any kind are welcome — bug fixes, features, docs. Please read [CONTRIBUTING.md](CONTRIBUTING.md) first (project layout, dev conventions, commit style, PR flow) and follow [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Security issues: report privately via [SECURITY.md](SECURITY.md).
|
|
164
|
+
|
|
88
165
|
## Credits
|
|
89
166
|
|
|
90
167
|
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) — the core.
|
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
[](https://github.com/Icather/dsh-clean-desktop-shell/releases/latest)
|
|
14
14
|
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
15
|
+
[](https://github.com/Icather/dsh-clean-desktop-shell/graphs/contributors)
|
|
15
16
|
|
|
16
17
|
</div>
|
|
17
18
|
|
|
@@ -49,13 +50,22 @@
|
|
|
49
50
|
|
|
50
51
|
| 平台 | 壳 | 状态 |
|
|
51
52
|
|:--|:--|:--|
|
|
52
|
-
| Windows | ✅ Electron(无边框 + 原生窗口按钮) |
|
|
53
|
-
| macOS | ✅ Electron(hiddenInset) |
|
|
53
|
+
| Windows | ✅ Electron(无边框 + 原生窗口按钮) | 已发布(NSIS 安装包) |
|
|
54
|
+
| macOS | ✅ Electron(hiddenInset) | 已发布(CI 构建 Intel + Apple Silicon DMG) |
|
|
54
55
|
| Linux | —(浏览器 / PWA 直连内核) | 不做 |
|
|
55
56
|
| Termux / 手机 / 平板 | —(headless / PWA 直连内核) | 由内核远程访问支持 |
|
|
56
57
|
|
|
57
58
|
## 安装
|
|
58
59
|
|
|
60
|
+
**方式一:从 Release 下载安装包(推荐普通用户)**
|
|
61
|
+
|
|
62
|
+
- Windows:下载 `DSH-Clean-Desktop-Shell-Setup-<版本>.exe`
|
|
63
|
+
- macOS:下载 `DSH-Clean-Desktop-Shell-<版本>.dmg`(Intel)或 `-arm64.dmg`(Apple Silicon)
|
|
64
|
+
|
|
65
|
+
首次运行 Windows 安装包可能触发 SmartScreen 警告——**这是未签名程序的正常现象,不是病毒**,见下方「Windows SmartScreen 警告说明」。
|
|
66
|
+
|
|
67
|
+
**方式二:作为 DSH 插件安装(开发者)**
|
|
68
|
+
|
|
59
69
|
```sh
|
|
60
70
|
dsh plugin --profile web add dsh-clean-desktop-shell
|
|
61
71
|
```
|
|
@@ -64,12 +74,63 @@ dsh plugin --profile web add dsh-clean-desktop-shell
|
|
|
64
74
|
|
|
65
75
|
> 桌面壳需要本机有可用的 `dsh web` 服务(或配置的远程地址)。见下方「使用」。
|
|
66
76
|
|
|
77
|
+
### Windows SmartScreen 警告说明
|
|
78
|
+
|
|
79
|
+
**为什么会看到警告?**
|
|
80
|
+
|
|
81
|
+
我们的安装包**没有代码签名证书**(个人开源项目暂未购买,证书年费约数百美元)。Windows 的 Microsoft Defender SmartScreen 是一个**信誉系统**——它根据"下载量 + 干净运行的记录"判断一个程序是否可信。对下载量少、未签名的 exe,它无法确认信誉,就会警告。**这不代表文件有病毒**:本项目完全开源,代码可审阅,也可自行校验 SHA-256(见下)。
|
|
82
|
+
|
|
83
|
+
**Edge 下载时会看到:**
|
|
84
|
+
|
|
85
|
+
下载面板里该文件被标记为"不常下载的文件",需要手动保留:
|
|
86
|
+
|
|
87
|
+
1. 悬停下载项,点击右侧的 `...` 菜单
|
|
88
|
+
2. 选择「保留」(Keep)
|
|
89
|
+
3. 弹窗确认,选择「仍要保留」(Keep anyway)
|
|
90
|
+
|
|
91
|
+
**双击安装时会看到:**
|
|
92
|
+
|
|
93
|
+
蓝色对话框「Windows 已保护你的电脑」——Microsoft Defender SmartScreen 阻止了无法识别的应用启动:
|
|
94
|
+
|
|
95
|
+
1. 点击「更多信息」(More info)
|
|
96
|
+
2. 核对文件名确实是 `DSH-Clean-Desktop-Shell-Setup-<版本>.exe`
|
|
97
|
+
3. 点击「仍要运行」(Run anyway)
|
|
98
|
+
|
|
99
|
+
**备选:一次性解除锁定(推荐)**
|
|
100
|
+
|
|
101
|
+
右键安装包 → 属性 → 常规 → 底部勾选「解除锁定」→ 确定。之后双击不再有警告。
|
|
102
|
+
|
|
103
|
+
或 PowerShell 批量解除:
|
|
104
|
+
|
|
105
|
+
```powershell
|
|
106
|
+
Unblock-File -Path "$env:USERPROFILE\Downloads\DSH-Clean-Desktop-Shell-Setup-*.exe"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**关于文件安全性的说明**
|
|
110
|
+
|
|
111
|
+
安装包由 GitHub Actions 从本仓库源码自动构建(见 `.github/workflows/build.yml`),代码完全开源可审阅。如仍有疑虑,可自行 `git clone` 后按「开发」一节本地构建比对,或稍等下载量积累——SmartScreen 信誉度上去后警告会自动消失。
|
|
112
|
+
|
|
113
|
+
> 说明:代码签名证书(EV 或 Azure Trusted Signing)可以彻底消除这个警告,但需要付费且对个人开源维护者不划算。本仓库会在条件允许时考虑接入签名。
|
|
114
|
+
|
|
67
115
|
## 使用
|
|
68
116
|
|
|
69
117
|
1. 启动 `dsh web`(或配置远程服务地址)。
|
|
70
118
|
2. 启动桌面壳:自动检测本地 3080;未运行时按配置拉起服务。
|
|
71
119
|
3. 窗口顶部(右侧留系统按钮)可拖动窗口;关闭默认缩到托盘。
|
|
72
120
|
|
|
121
|
+
**后端的一切操作都在托盘右键**,主窗口保持纯壳:
|
|
122
|
+
|
|
123
|
+
- 启动 / 重启 / 关闭后端(带进度弹窗;关闭会真正停掉 3080 上的服务,包括外部启动的实例)
|
|
124
|
+
- 自动探测后端 · 设置后端安装文件夹(默认自动探测定位)
|
|
125
|
+
- 刷新窗口 · 检查更新 · 仓库主页 · 开机自启
|
|
126
|
+
|
|
127
|
+
**窗口的可靠性(Edge 式即时刷新)**:
|
|
128
|
+
|
|
129
|
+
- 双击启动立即出窗,不等后端就绪
|
|
130
|
+
- 后端没起来时显示「后端未连接」页,自动探测;后端一通立即加载
|
|
131
|
+
- 后端关闭 / 被杀的一刻,窗口立刻切回离线页——不会停在旧页面假装还活着
|
|
132
|
+
- 离线页内置快捷按钮:重新加载 / 启动后端 / 自动探测后端 / 设置后端安装文件夹
|
|
133
|
+
|
|
73
134
|
## 开发
|
|
74
135
|
|
|
75
136
|
```sh
|
|
@@ -81,9 +142,19 @@ npm run pack # 打包 NSIS (Win) / DMG (mac)
|
|
|
81
142
|
|
|
82
143
|
## 更新历史
|
|
83
144
|
|
|
145
|
+
### 0.1.1
|
|
146
|
+
- 后端生命周期:修复 Windows 下启动报 `spawn EINVAL`、卡「启动中」的问题;「关闭后端」现在能真正停掉后端(含外部启动的实例);启动 / 重启 / 关闭带进度弹窗。
|
|
147
|
+
- 窗口可靠性:双击立即出窗;后端关闭窗口立刻切离线黑屏;后端恢复自动重连(Edge 式即时刷新)。
|
|
148
|
+
- 离线页自助:重新加载 / 启动后端 / 自动探测后端 / 设置后端安装文件夹。
|
|
149
|
+
- 托盘:新增「刷新窗口」;macOS 构建发布(Intel + Apple Silicon DMG)。
|
|
150
|
+
|
|
84
151
|
### 0.1.0
|
|
85
152
|
- 初始版本:Electron 壳骨架,系统托盘/单实例/开机自启,DSH 插件挂载。
|
|
86
153
|
|
|
154
|
+
## 贡献
|
|
155
|
+
|
|
156
|
+
欢迎任何形式的贡献——修 bug、加功能、改进文档都行。请先阅读 [CONTRIBUTING.md](CONTRIBUTING.md)(含项目结构、开发约定、提交规范、PR 流程),并遵守 [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)。安全漏洞请走 [SECURITY.md](SECURITY.md) 的私密报告流程。
|
|
157
|
+
|
|
87
158
|
## 致谢
|
|
88
159
|
|
|
89
160
|
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) —— 内核本体。
|
package/electron/config.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Fields:
|
|
5
5
|
* - targetUrl: default 'http://127.0.0.1:3080' — set to any remote
|
|
6
6
|
* DSH address to run the shell as a pure window.
|
|
7
|
-
* - autoStartService: default true — only applies to the local default URL.
|
|
8
7
|
* - closeToTray: default true.
|
|
9
8
|
* - windowMode: 'advanced' (mica/vibrancy) | 'compatibility'.
|
|
10
9
|
* - autoLaunch: open at login.
|
|
@@ -15,7 +14,6 @@ import { dirname, join } from 'node:path'
|
|
|
15
14
|
|
|
16
15
|
const DEFAULTS = {
|
|
17
16
|
targetUrl: 'http://127.0.0.1:3080',
|
|
18
|
-
autoStartService: true,
|
|
19
17
|
closeToTray: true,
|
|
20
18
|
windowMode: 'advanced',
|
|
21
19
|
autoLaunch: false,
|
package/electron/main.js
CHANGED
|
@@ -12,7 +12,8 @@ import { app, BrowserWindow } from 'electron'
|
|
|
12
12
|
import { createMainWindow, reloadWindow } from './window.js'
|
|
13
13
|
import { createTray, refreshTrayMenu } from './tray.js'
|
|
14
14
|
import { loadConfig, saveConfig } from './config.js'
|
|
15
|
-
import { detect
|
|
15
|
+
import { detect } from './service.js'
|
|
16
|
+
import { setupAutoUpdater } from './update.js'
|
|
16
17
|
|
|
17
18
|
const isMac = process.platform === 'darwin'
|
|
18
19
|
|
|
@@ -56,22 +57,18 @@ if (!gotLock) {
|
|
|
56
57
|
return mainWindow
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Launch-time backend detection. Detects whether a local dsh web is
|
|
62
|
+
* already running so the tray and window reflect the real state, but
|
|
63
|
+
* never auto-starts it — starting is a manual action (tray menu or the
|
|
64
|
+
* offline screen button) so it cannot fight an explicit "stop".
|
|
65
|
+
*/
|
|
60
66
|
async function bootstrapBackend() {
|
|
61
67
|
const config = loadConfig()
|
|
62
68
|
const target = config.targetUrl || 'http://127.0.0.1:3080'
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
const url = await detect()
|
|
67
|
-
if (!url) {
|
|
68
|
-
// Not running — try to start it, but never block window opening.
|
|
69
|
-
try {
|
|
70
|
-
await start({ backendPath: config.backendPath })
|
|
71
|
-
} catch {
|
|
72
|
-
// Backend unavailable; window still opens, tray shows the error.
|
|
73
|
-
}
|
|
74
|
-
}
|
|
69
|
+
// Remote targets are the user's own business — never probe local.
|
|
70
|
+
if (target !== 'http://127.0.0.1:3080') return
|
|
71
|
+
await detect()
|
|
75
72
|
// Reflect the post-detect state in the tray menu.
|
|
76
73
|
refreshTrayMenu()
|
|
77
74
|
}
|
|
@@ -103,6 +100,9 @@ if (!gotLock) {
|
|
|
103
100
|
// Backend bootstrap in background (never delays the window).
|
|
104
101
|
bootstrapBackend().catch(() => {})
|
|
105
102
|
|
|
103
|
+
// Windows: wire the auto-updater (downloads new installers silently).
|
|
104
|
+
setupAutoUpdater()
|
|
105
|
+
|
|
106
106
|
app.on('activate', () => {
|
|
107
107
|
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
|
108
108
|
else mainWindow?.show()
|
package/electron/tray.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
onStatusChange,
|
|
20
20
|
} from './service.js'
|
|
21
21
|
import { showProgress, setProgress, closeProgress } from './progress.js'
|
|
22
|
-
import { checkForUpdate, openRepo, openUrl } from './update.js'
|
|
22
|
+
import { checkForUpdate, checkForUpdatesAuto, isAutoUpdateSupported, openRepo, openUrl } from './update.js'
|
|
23
23
|
|
|
24
24
|
const trayIconPath = join(
|
|
25
25
|
fileURLToPath(new URL('.', import.meta.url)),
|
|
@@ -115,6 +115,12 @@ export function refreshTrayMenu() {
|
|
|
115
115
|
{
|
|
116
116
|
label: '检查更新…',
|
|
117
117
|
click: async () => {
|
|
118
|
+
// Windows (packaged): auto-download + install on restart.
|
|
119
|
+
// macOS / dev mode: manual page link (macOS needs a signature).
|
|
120
|
+
if (isAutoUpdateSupported()) {
|
|
121
|
+
await checkForUpdatesAuto()
|
|
122
|
+
return
|
|
123
|
+
}
|
|
118
124
|
const r = await checkForUpdate()
|
|
119
125
|
if (r.hasUpdate) {
|
|
120
126
|
const choice = dialog.showMessageBoxSync({
|
package/electron/update.js
CHANGED
|
@@ -1,15 +1,142 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Update handling — Windows auto-update, macOS manual download.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Windows (packaged app): uses electron-updater to download the new
|
|
5
|
+
* installer from GitHub Releases in the background and install it on
|
|
6
|
+
* restart. Progress is shown in the small progress window.
|
|
7
|
+
*
|
|
8
|
+
* macOS / dev mode: falls back to a manual check that opens the GitHub
|
|
9
|
+
* Releases page (macOS auto-update needs a Developer ID signature which
|
|
10
|
+
* this project does not have yet).
|
|
7
11
|
*/
|
|
8
|
-
import { app, shell } from 'electron'
|
|
12
|
+
import { app, shell, dialog } from 'electron'
|
|
13
|
+
import { showProgress, setProgress, closeProgress } from './progress.js'
|
|
9
14
|
|
|
10
15
|
const REPO_URL = 'https://github.com/Icather/dsh-clean-desktop-shell'
|
|
11
16
|
const RELEASES_API = 'https://api.github.com/repos/Icather/dsh-clean-desktop-shell/releases/latest'
|
|
12
17
|
|
|
18
|
+
let autoUpdater = null
|
|
19
|
+
let updaterPromise = null
|
|
20
|
+
|
|
21
|
+
/** Lazy-load electron-updater (CJS → ESM interop via dynamic import). */
|
|
22
|
+
function getAutoUpdater() {
|
|
23
|
+
if (!updaterPromise) {
|
|
24
|
+
updaterPromise = import('electron-updater').then((m) => m.autoUpdater)
|
|
25
|
+
}
|
|
26
|
+
return updaterPromise
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Windows packaged apps can auto-update; everything else uses manual. */
|
|
30
|
+
export function isAutoUpdateSupported() {
|
|
31
|
+
return process.platform === 'win32' && app.isPackaged
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Wire the auto-updater events once (no-op on mac / dev mode). */
|
|
35
|
+
export function setupAutoUpdater() {
|
|
36
|
+
if (!isAutoUpdateSupported()) return
|
|
37
|
+
getAutoUpdater()
|
|
38
|
+
.then((au) => {
|
|
39
|
+
if (autoUpdater) return
|
|
40
|
+
autoUpdater = au
|
|
41
|
+
autoUpdater.autoDownload = true
|
|
42
|
+
autoUpdater.autoInstallOnAppQuit = true
|
|
43
|
+
|
|
44
|
+
autoUpdater.on('update-available', () => {
|
|
45
|
+
showProgress({ title: '检查更新', message: '发现新版本,正在后台下载…' })
|
|
46
|
+
})
|
|
47
|
+
autoUpdater.on('download-progress', (p) => {
|
|
48
|
+
const pct = Math.round(p.percent)
|
|
49
|
+
setProgress({
|
|
50
|
+
title: '检查更新',
|
|
51
|
+
message: `正在下载更新:${pct}%`,
|
|
52
|
+
state: pct >= 100 ? 'ok' : 'busy',
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
autoUpdater.on('update-downloaded', () => {
|
|
56
|
+
setProgress({ title: '检查更新', message: '更新已下载', state: 'ok' })
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
closeProgress()
|
|
59
|
+
const choice = dialog.showMessageBoxSync({
|
|
60
|
+
type: 'info',
|
|
61
|
+
title: '更新已就绪',
|
|
62
|
+
message: '新版本已下载完成,重启应用即可安装。',
|
|
63
|
+
detail: '是否立即重启安装?',
|
|
64
|
+
buttons: ['立即重启', '稍后'],
|
|
65
|
+
defaultId: 0,
|
|
66
|
+
cancelId: 1,
|
|
67
|
+
})
|
|
68
|
+
if (choice === 0) autoUpdater.quitAndInstall()
|
|
69
|
+
}, 800)
|
|
70
|
+
})
|
|
71
|
+
autoUpdater.on('update-not-available', () => {
|
|
72
|
+
closeProgress()
|
|
73
|
+
dialog.showMessageBoxSync({
|
|
74
|
+
type: 'info',
|
|
75
|
+
title: '已是最新版本',
|
|
76
|
+
message: '当前已是最新版本。',
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
autoUpdater.on('error', (err) => {
|
|
80
|
+
closeProgress()
|
|
81
|
+
dialog.showMessageBoxSync({
|
|
82
|
+
type: 'warning',
|
|
83
|
+
title: '检查更新失败',
|
|
84
|
+
message: `自动更新失败:${err?.message || '未知错误'}`,
|
|
85
|
+
detail: '可前往 GitHub Releases 手动下载。',
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
.catch(() => {
|
|
90
|
+
// electron-updater failed to load — auto-update silently disabled.
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Tray "check for update": auto flow on Windows, manual fallback elsewhere. */
|
|
95
|
+
export async function checkForUpdatesAuto() {
|
|
96
|
+
if (isAutoUpdateSupported()) {
|
|
97
|
+
try {
|
|
98
|
+
await getAutoUpdater()
|
|
99
|
+
await autoUpdater?.checkForUpdates()
|
|
100
|
+
} catch (err) {
|
|
101
|
+
closeProgress()
|
|
102
|
+
dialog.showMessageBoxSync({
|
|
103
|
+
type: 'warning',
|
|
104
|
+
title: '检查更新失败',
|
|
105
|
+
message: `自动更新失败:${err?.message || '未知错误'}`,
|
|
106
|
+
detail: '可前往 GitHub Releases 手动下载。',
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Manual path (macOS / dev mode): compare versions, offer GitHub page.
|
|
113
|
+
const r = await checkForUpdate()
|
|
114
|
+
if (r.hasUpdate) {
|
|
115
|
+
const choice = dialog.showMessageBoxSync({
|
|
116
|
+
type: 'info',
|
|
117
|
+
title: '发现新版本',
|
|
118
|
+
message: `当前版本 ${r.current},最新版本 ${r.latest}。`,
|
|
119
|
+
detail: 'macOS 自动更新需要代码签名,当前请前往 GitHub Releases 手动下载。',
|
|
120
|
+
buttons: ['前往下载', '取消'],
|
|
121
|
+
defaultId: 0,
|
|
122
|
+
cancelId: 1,
|
|
123
|
+
})
|
|
124
|
+
if (choice === 0) openUrl(r.url)
|
|
125
|
+
} else if (r.latest) {
|
|
126
|
+
dialog.showMessageBoxSync({
|
|
127
|
+
type: 'info',
|
|
128
|
+
title: '已是最新版本',
|
|
129
|
+
message: `当前版本 ${r.current} 已是最新(${r.latest})。`,
|
|
130
|
+
})
|
|
131
|
+
} else {
|
|
132
|
+
dialog.showMessageBoxSync({
|
|
133
|
+
type: 'warning',
|
|
134
|
+
title: '检查更新失败',
|
|
135
|
+
message: '无法连接 GitHub 检查更新,请检查网络后重试。',
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
13
140
|
/** Parse "v1.2.3" / "1.2.3" → [1,2,3]; null when malformed. */
|
|
14
141
|
function parseVersion(v) {
|
|
15
142
|
if (!v) return null
|
|
@@ -27,7 +154,7 @@ function isNewer(a, b) {
|
|
|
27
154
|
}
|
|
28
155
|
|
|
29
156
|
/**
|
|
30
|
-
*
|
|
157
|
+
* Manual version check against the latest GitHub release (macOS path).
|
|
31
158
|
* @returns {{ hasUpdate: boolean, latest?: string, current: string, url: string }}
|
|
32
159
|
*/
|
|
33
160
|
export async function checkForUpdate() {
|
package/package.json
CHANGED
|
@@ -1,104 +1,112 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-clean-desktop-shell",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Clean desktop shell for DeepSeek Harness (DSH) as a DSH plugin — reuses your web profile, tray/single-instance/auto-launch, zero visual changes. DSH 插件形态的纯净桌面壳:复用现有 web profile,托盘管理后端,零视觉改造。",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "lib/index.js",
|
|
7
|
-
"files": [
|
|
8
|
-
"lib/",
|
|
9
|
-
"electron/",
|
|
10
|
-
"src/",
|
|
11
|
-
"scripts/",
|
|
12
|
-
"build/icon.png",
|
|
13
|
-
"cordis.patch.yml",
|
|
14
|
-
"README.md",
|
|
15
|
-
"README.en.md",
|
|
16
|
-
"LICENSE",
|
|
17
|
-
"version.txt"
|
|
18
|
-
],
|
|
19
|
-
"exports": {
|
|
20
|
-
".": "./lib/index.js",
|
|
21
|
-
"./client": "./lib/client.js",
|
|
22
|
-
"./package.json": "./package.json"
|
|
23
|
-
},
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build": "node scripts/build.mjs",
|
|
26
|
-
"check": "node --check lib/index.js",
|
|
27
|
-
"dev": "electron electron/main.js",
|
|
28
|
-
"icons": "node scripts/gen-icons.mjs",
|
|
29
|
-
"pack": "electron-builder --win nsis",
|
|
30
|
-
"pack:mac": "electron-builder --mac dmg"
|
|
31
|
-
},
|
|
32
|
-
"dsh": {
|
|
33
|
-
"bundle": {
|
|
34
|
-
"patch": "./cordis.patch.yml"
|
|
35
|
-
},
|
|
36
|
-
"client": {
|
|
37
|
-
"inject": [
|
|
38
|
-
"@deepseek-ai/dsh-client-runtime",
|
|
39
|
-
"@deepseek-ai/dsh-client-locale",
|
|
40
|
-
"@deepseek-ai/dsh-client-ui-settings",
|
|
41
|
-
"@deepseek-ai/dsh-client-ui-theme"
|
|
42
|
-
],
|
|
43
|
-
"platform": "web"
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
"peerDependencies": {
|
|
47
|
-
"@deepseek-ai/dsh": "*"
|
|
48
|
-
},
|
|
49
|
-
"peerDependenciesMeta": {
|
|
50
|
-
"@deepseek-ai/dsh": {
|
|
51
|
-
"optional": true
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
"devDependencies": {
|
|
55
|
-
"electron": "^33.0.0",
|
|
56
|
-
"electron-builder": "^25.0.0",
|
|
57
|
-
"png-to-ico": "^3.0.2",
|
|
58
|
-
"sharp": "^0.35.3"
|
|
59
|
-
},
|
|
60
|
-
"build": {
|
|
61
|
-
"appId": "com.icather.dsh-clean-desktop-shell",
|
|
62
|
-
"productName": "DSH Clean Desktop Shell",
|
|
63
|
-
"asar": true,
|
|
64
|
-
"files": [
|
|
65
|
-
"electron/**/*",
|
|
66
|
-
"lib/**/*",
|
|
67
|
-
"src/**/*",
|
|
68
|
-
"build/icon.png",
|
|
69
|
-
"package.json",
|
|
70
|
-
"cordis.patch.yml"
|
|
71
|
-
],
|
|
72
|
-
"extraMetadata": {
|
|
73
|
-
"main": "electron/main.js"
|
|
74
|
-
},
|
|
75
|
-
"win": {
|
|
76
|
-
"target": [
|
|
77
|
-
{
|
|
78
|
-
"target": "nsis",
|
|
79
|
-
"arch": [
|
|
80
|
-
"x64"
|
|
81
|
-
]
|
|
82
|
-
}
|
|
83
|
-
],
|
|
84
|
-
"icon": "build/icon.ico",
|
|
85
|
-
"signAndEditExecutable": false
|
|
86
|
-
},
|
|
87
|
-
"mac": {
|
|
88
|
-
"target": [
|
|
89
|
-
"dmg"
|
|
90
|
-
],
|
|
91
|
-
"category": "public.app-category.developer-tools"
|
|
92
|
-
},
|
|
93
|
-
"nsis": {
|
|
94
|
-
"oneClick":
|
|
95
|
-
"
|
|
96
|
-
"createDesktopShortcut": true,
|
|
97
|
-
"createStartMenuShortcut": true
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-clean-desktop-shell",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Clean desktop shell for DeepSeek Harness (DSH) as a DSH plugin — reuses your web profile, tray/single-instance/auto-launch, zero visual changes. DSH 插件形态的纯净桌面壳:复用现有 web profile,托盘管理后端,零视觉改造。",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib/",
|
|
9
|
+
"electron/",
|
|
10
|
+
"src/",
|
|
11
|
+
"scripts/",
|
|
12
|
+
"build/icon.png",
|
|
13
|
+
"cordis.patch.yml",
|
|
14
|
+
"README.md",
|
|
15
|
+
"README.en.md",
|
|
16
|
+
"LICENSE",
|
|
17
|
+
"version.txt"
|
|
18
|
+
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./lib/index.js",
|
|
21
|
+
"./client": "./lib/client.js",
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "node scripts/build.mjs",
|
|
26
|
+
"check": "node --check lib/index.js",
|
|
27
|
+
"dev": "electron electron/main.js",
|
|
28
|
+
"icons": "node scripts/gen-icons.mjs",
|
|
29
|
+
"pack": "electron-builder --win nsis",
|
|
30
|
+
"pack:mac": "electron-builder --mac dmg"
|
|
31
|
+
},
|
|
32
|
+
"dsh": {
|
|
33
|
+
"bundle": {
|
|
34
|
+
"patch": "./cordis.patch.yml"
|
|
35
|
+
},
|
|
36
|
+
"client": {
|
|
37
|
+
"inject": [
|
|
38
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
39
|
+
"@deepseek-ai/dsh-client-locale",
|
|
40
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
41
|
+
"@deepseek-ai/dsh-client-ui-theme"
|
|
42
|
+
],
|
|
43
|
+
"platform": "web"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@deepseek-ai/dsh": "*"
|
|
48
|
+
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"@deepseek-ai/dsh": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"electron": "^33.0.0",
|
|
56
|
+
"electron-builder": "^25.0.0",
|
|
57
|
+
"png-to-ico": "^3.0.2",
|
|
58
|
+
"sharp": "^0.35.3"
|
|
59
|
+
},
|
|
60
|
+
"build": {
|
|
61
|
+
"appId": "com.icather.dsh-clean-desktop-shell",
|
|
62
|
+
"productName": "DSH Clean Desktop Shell",
|
|
63
|
+
"asar": true,
|
|
64
|
+
"files": [
|
|
65
|
+
"electron/**/*",
|
|
66
|
+
"lib/**/*",
|
|
67
|
+
"src/**/*",
|
|
68
|
+
"build/icon.png",
|
|
69
|
+
"package.json",
|
|
70
|
+
"cordis.patch.yml"
|
|
71
|
+
],
|
|
72
|
+
"extraMetadata": {
|
|
73
|
+
"main": "electron/main.js"
|
|
74
|
+
},
|
|
75
|
+
"win": {
|
|
76
|
+
"target": [
|
|
77
|
+
{
|
|
78
|
+
"target": "nsis",
|
|
79
|
+
"arch": [
|
|
80
|
+
"x64"
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"icon": "build/icon.ico",
|
|
85
|
+
"signAndEditExecutable": false
|
|
86
|
+
},
|
|
87
|
+
"mac": {
|
|
88
|
+
"target": [
|
|
89
|
+
"dmg"
|
|
90
|
+
],
|
|
91
|
+
"category": "public.app-category.developer-tools"
|
|
92
|
+
},
|
|
93
|
+
"nsis": {
|
|
94
|
+
"oneClick": true,
|
|
95
|
+
"perMachine": false,
|
|
96
|
+
"createDesktopShortcut": true,
|
|
97
|
+
"createStartMenuShortcut": true
|
|
98
|
+
},
|
|
99
|
+
"publish": {
|
|
100
|
+
"provider": "github",
|
|
101
|
+
"owner": "Icather",
|
|
102
|
+
"repo": "dsh-clean-desktop-shell"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"license": "MIT",
|
|
106
|
+
"allowScripts": {
|
|
107
|
+
"electron@33.4.11": true
|
|
108
|
+
},
|
|
109
|
+
"dependencies": {
|
|
110
|
+
"electron-updater": "^6.8.9"
|
|
111
|
+
}
|
|
112
|
+
}
|
package/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.2
|