dsh-app-updater 1.0.0 → 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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [1.2.0] — 2026-10-31
6
+
7
+ ### 新增
8
+
9
+ - **运行环境识别**(`lib/platform.js`):识别 Windows / macOS / 其它系统、具体版本与名称
10
+ (`Windows 11(build 26300)`、`macOS 15(Sequoia)`)、CPU 架构(ARM64 / x64 / Intel / x86),
11
+ 并记录 CPU 型号与线程数。Windows on ARM 上跑 x64 的 Node 时,靠 `PROCESSOR_ARCHITEW6432`
12
+ 仍能还原出真实架构,而不是被骗成 x64。
13
+ - **按架构挑选安装包**:Windows 侧把识别到的架构传给 winget 的 `--architecture`,让「官网全量包」
14
+ 与当前系统/芯片匹配;清单里没有该架构的安装包时自动退回自动匹配,并在日志里说明。
15
+ macOS 侧由 Homebrew 按本机架构取对应 cask。新增开关「按本机架构安装包」(默认开启)。
16
+ - **macOS 更新引擎(Homebrew cask)**:用 `brew list --cask --versions` 拿已装版本、
17
+ `brew outdated --json=v2 --cask --greedy` 拿可升级版本,`brew upgrade --cask <token>` 更新;
18
+ 演练模式改用 `brew fetch --cask <token>`,只下载安装包不安装。没有 Homebrew 时明确报告
19
+ 「不可用」,而不是假装能更新。
20
+ - **界面**:状态行与设置面板新增「运行环境」(系统 · 架构 · 引擎);引擎不可用时红字给出原因;
21
+ 应用来源新增 `homebrew` 徽章。
22
+ - `/health.json` 增加 `version` / `env` / `envSummary` / `engine` / `capabilities`;
23
+ `state.json` 同步带出这些字段。
24
+
25
+ ### 说明
26
+
27
+ - macOS 路径无法在开发机(Windows)上真机验证,因此识别与 Homebrew 输出解析均以注入式单测覆盖
28
+ (`test/unit.mjs` 共 66 项),安装/更新命令按 Homebrew 官方 CLI 行为编写。
29
+ - 扫描/更新接口按平台放行:Windows 与 macOS 通过,其它系统返回 400 并说明原因。
30
+
5
31
  ## [1.0.0] — 2026-10-31
6
32
 
7
33
  首个可用版本。
package/CONTRACT.md CHANGED
@@ -10,7 +10,8 @@ dsh-app-updater/
10
10
  ├─ cordis.patch.yml # insert 行:id=dsh-app-updater, name=dsh-app-updater
11
11
  ├─ lib/
12
12
  │ ├─ index.js # 宿主半身:cordis 插件,注册 /dsh-app-updater/* 路由
13
- │ ├─ engine.js # 扫描 / 下载 / 安装编排
13
+ │ ├─ platform.js # 运行环境识别:Windows / macOS、版本、CPU 架构、引擎选择
14
+ │ ├─ engine.js # 扫描 / 下载 / 安装编排(Windows=winget / macOS=Homebrew)
14
15
  │ ├─ winget.js # winget 定位、调用、表格解析
15
16
  │ ├─ registry.js # 注册表 Uninstall 键枚举(覆盖 winget 未收录的应用)
16
17
  │ ├─ version.js # 版本比较(Windows 风格 1.2.3.4 + 字母后缀)
@@ -19,6 +20,14 @@ dsh-app-updater/
19
20
  └─ README.md
20
21
  ```
21
22
 
23
+ ## 运行环境与引擎
24
+
25
+ - `platform.js` 输出 `env`:`{ platform, family: windows|macos|other, osName, osVersion, osBuild, arch: arm64|x64|x86, archLabel, wingetArch, cpuModel, cpuCount }`。
26
+ - 引擎:Windows → `winget`;macOS → `homebrew`;其它 → `none`(扫描/更新接口返回 400)。
27
+ - 架构匹配:`settings.matchSystemArch !== false` 时,Windows 侧把 `env.wingetArch` 传给
28
+ `winget download/upgrade --architecture`;清单缺该架构时自动退回自动匹配并写日志。
29
+ macOS 侧由 Homebrew 决定 cask 架构。
30
+
22
31
  ## 宿主 HTTP 路由
23
32
 
24
33
  全部 `kind: "exact"`,前缀 `/dsh-app-updater/`。写操作要求 loopback + `confirm: true`。
@@ -29,13 +38,17 @@ dsh-app-updater/
29
38
  {
30
39
  "ok": true,
31
40
  "platform": "win32",
41
+ "env": { "platform": "win32", "family": "windows", "osName": "Windows 11", "osVersion": "10.0.26300", "osBuild": "26300", "arch": "x64", "archLabel": "x64(AMD64)", "wingetArch": "x64", "cpuModel": "13th Gen Intel(R) Core(TM) i7-13700K", "cpuCount": 24 },
42
+ "envSummary": "Windows 11(build 26300) · x64(AMD64)",
43
+ "engine": { "id": "winget", "available": true, "version": "1.29.380" },
44
+ "capabilities": { "scan": true, "update": true, "checkVersions": true, "storeAppsIncluded": true, "note": "" },
32
45
  "winget": { "available": true, "version": "v1.29.380", "path": "C:\\...\\winget.exe" },
33
46
  "phase": "idle", // idle | scanning | updating
34
47
  "scan": { "running": false, "startedAt": 0, "finishedAt": 0, "error": null },
35
48
  "update": { "running": false, "total": 0, "done": 0, "current": null, "error": null },
36
- "apps": { "scannedAt": 0, "stats": { "total": 0, "upgradable": 0, "unsupported": 0 }, "items": [] },
49
+ "apps": { "scannedAt": 0, "stats": { "total": 0, "upgradable": 0, "unsupported": 0 }, "items": [], "engine": "winget" },
37
50
  "log": [ { "at": 0, "level": "info|warn|error|success", "text": "..." } ],
38
- "settings": { "includeStoreApps": true, "includeRegistryOnly": true, "downloadDir": "", "keepInstallers": true, "hashFallbackUpgrade": true, "dryRun": false }
51
+ "settings": { "includeStoreApps": true, "includeRegistryOnly": true, "downloadDir": "", "keepInstallers": true, "hashFallbackUpgrade": true, "dryRun": false, "matchSystemArch": true }
39
52
  }
40
53
  ```
41
54
 
@@ -43,17 +56,17 @@ dsh-app-updater/
43
56
 
44
57
  ```jsonc
45
58
  {
46
- "id": "Google.Chrome", // winget id;纯注册表项为 "reg:<GUID 或 key 尾段>"
59
+ "id": "Google.Chrome", // winget id;注册表项 "reg:<key>";macOS "brew:<cask token>"
47
60
  "name": "Google Chrome",
48
61
  "version": "155.0.8059.85", // 已安装版本,未知时 ""
49
62
  "available": "156.0.8078.48", // 可升级到的版本,无更新时 ""
50
63
  "publisher": "Google LLC",
51
- "source": "winget", // winget | msstore | registry
64
+ "source": "winget", // winget | msstore | registry | homebrew
52
65
  "installLocation": "C:\\Program Files\\Google\\Chrome\\Application",
53
66
  "hasUpdate": true,
54
67
  "updateSupported": true, // 本插件能否驱动更新
55
68
  "reason": null, // null | "not-in-catalog" | "unknown-version" | "pinned" | "store-managed" | "system-component"
56
- "kind": "exe" // exe | msi | msix | inno | null(未知)
69
+ "kind": "exe" // exe | msi | msix | inno | cask | null(未知)
57
70
  }
58
71
  ```
59
72
 
package/README.en.md CHANGED
@@ -1,28 +1,34 @@
1
1
  # dsh-app-updater — App Updater for DeepSeek Harness
2
2
 
3
3
  **English** | [简体中文](README.md)
4
- ![version](https://img.shields.io/badge/version-1.0.0-4b6fff?style=flat-square)
4
+ [![npm](https://img.shields.io/npm/v/dsh-app-updater?style=flat-square&label=npm&color=0891b2&logo=npm)](https://www.npmjs.com/package/dsh-app-updater)
5
5
  ![license](https://img.shields.io/badge/license-MIT-2f6f4f?style=flat-square)
6
6
  ![platform](https://img.shields.io/badge/platform-Windows-7da1de?style=flat-square)
7
7
  ![dsh-plugin](https://img.shields.io/badge/DSH-plugin-263146?style=flat-square)
8
8
 
9
9
 
10
- Detect every installed Windows application that has a newer version, then update them
10
+ Detect every installed application that has a newer version, then update them
11
11
  with one click — downloading the **full installer from each vendor's own website** and
12
12
  installing it silently. **The detected applications are never launched.**
13
+ Windows (winget) and macOS (Homebrew), architecture-aware: ARM64 and x64/Intel installers are
14
+ chosen to match the machine.
13
15
 
14
16
  Where to find it: DeepSeek Harness → Settings → **应用更新 (App Updater)**
15
17
 
16
18
  ## Install
17
19
 
18
20
  ```bash
19
- # straight from the source repository
20
- dsh plugin --profile web add github:19952730389/dsh-app-updater
21
+ # npm package (recommended; also one-click installable from the plugin center)
22
+ dsh plugin --profile web add dsh-app-updater
21
23
  ```
22
24
 
23
- Prebuilt `.tgz` (see [Releases](https://github.com/19952730389/dsh-app-updater/releases)):
25
+ Without npm, install from source or from the prebuilt tarball:
24
26
 
25
27
  ```bash
28
+ # source repository
29
+ dsh plugin --profile web add github:19952730389/dsh-app-updater
30
+
31
+ # prebuilt tgz (see Releases)
26
32
  dsh plugin --profile web add https://github.com/19952730389/dsh-app-updater/releases/latest/download/dsh-app-updater.tgz
27
33
  ```
28
34
 
@@ -88,6 +94,7 @@ State file: `%USERPROFILE%\.dsh\dsh-app-updater\state.json`
88
94
  | `keepInstallers` | `true` | Keep the installer after a successful update. |
89
95
  | `hashFallbackUpgrade` | `true` | Fall back to `winget upgrade` when the download fails. |
90
96
  | `dryRun` | `false` | **Rehearsal mode**: download the vendor's full installer but never install it. |
97
+ | `matchSystemArch` | `true` | Match the installer to the detected system and CPU architecture (Windows passes `--architecture`; macOS is resolved by Homebrew). |
91
98
 
92
99
  Everything above is editable from the plugin's own settings panel inside the settings page.
93
100
  The panel also accepts API calls:
@@ -113,18 +120,41 @@ curl -X POST -H "Content-Type: application/json" \
113
120
 
114
121
  ## Requirements
115
122
 
116
- - **Windows** with `winget.exe` (Microsoft App Installer). Without winget the plugin can still
123
+ - **Windows**: `winget.exe` (Microsoft App Installer). Without winget the plugin can still
117
124
  list registry apps, but cannot tell whether they are up to date.
125
+ - **macOS**: `brew` ([Homebrew](https://brew.sh)). Without Homebrew the plugin reports the engine
126
+ as unavailable instead of pretending it can update anything.
118
127
  - No third-party npm dependencies: the host half uses Node built-ins only, the browser half
119
128
  only the host-provided `react`.
120
129
 
130
+ ## Supported systems and architectures
131
+
132
+ The plugin detects its own environment at startup and picks the installer that matches it:
133
+
134
+ | System | Architecture | Engine | Notes |
135
+ |---|---|---|---|
136
+ | Windows 10 / 11 | x64 / ARM64 / x86 | **winget** | the detected architecture is passed as `winget --architecture`; if the manifest has no installer for it, the plugin falls back to automatic matching and says so in the log |
137
+ | macOS 11+ | Apple Silicon / Intel | **Homebrew casks** | Homebrew itself resolves the right cask for the machine; without Homebrew the engine is reported unavailable |
138
+ | anything else | — | none | the scan/update routes answer 400 with the reason |
139
+
140
+ The result is shown in the status line and in the settings panel's “运行环境 / Environment” row, e.g.
141
+ `Windows 11(build 26300) · x64(AMD64) · winget 1.29.380` or
142
+ `macOS 15(Sequoia) · Apple Silicon(ARM64) · Homebrew 4.5.0`.
143
+
144
+ The “按本机架构安装包 / match system architecture” switch (on by default) can be turned off to hand
145
+ the choice back to winget; Homebrew always resolves for the local architecture.
146
+
147
+ > The macOS/Homebrew path follows Homebrew's documented CLI behaviour and its detection and parsing
148
+ > logic are covered by unit tests, but it has not been verified on a real Mac — this plugin is
149
+ > developed on Windows. Please open an issue if something misbehaves there.
150
+
121
151
  ## HTTP API (host half)
122
152
 
123
153
  | Method | Path | Purpose |
124
154
  |---|---|---|
125
155
  | GET | `/dsh-app-updater/state.json` | Status, inventory, log, settings |
126
156
  | GET | `/dsh-app-updater/apps.json` | Inventory and statistics only |
127
- | GET | `/dsh-app-updater/health.json` | Self-check (route count, data directory) |
157
+ | GET | `/dsh-app-updater/health.json` | Self-check: version, environment, engine, capabilities, route count, data directory |
128
158
  | POST | `/dsh-app-updater/scan` | `{confirm:true, force:true}` start scanning |
129
159
  | POST | `/dsh-app-updater/update` | `{confirm:true, ids:[...]}` start the update queue |
130
160
  | POST | `/dsh-app-updater/cancel` | `{confirm:true}` cancel items not yet started |
@@ -153,7 +183,7 @@ Then add `dsh-app-updater` to `dsh.profile.bundles` in the profile's `package.js
153
183
  ## Tests
154
184
 
155
185
  ```bash
156
- node test/unit.mjs # 36 checks: table parsing, version compare, manifest & switch parsing
186
+ node test/unit.mjs # 66 checks: table parsing, version compare, manifest & switches, OS/arch detection, Homebrew parsing
157
187
  node test/render.mjs # 32 checks: settings panel, rehearsal banner, column drag, request bodies
158
188
  node test/harness.mjs --scan # boots the host half against a fake cordis ctx + real HTTP
159
189
  ```
package/README.md CHANGED
@@ -1,27 +1,32 @@
1
1
  # dsh-app-updater — 应用更新(DSH 插件)
2
2
 
3
- 检测本机**所有已安装的 Windows 应用**是否有新版本,并从各自**官网下载完整安装包**一键静默更新。
3
+ 检测本机**所有已安装应用**是否有新版本,并从各自**官网下载完整安装包**一键静默更新。
4
4
  检测过程**不需要打开被检测的软件**。
5
+ 自动识别 **Windows / macOS** 与 **ARM64 / x64(Intel)** 架构,按当前系统与芯片挑选对应的安装包。
5
6
 
6
7
  入口:DeepSeek Harness → 设置 → **应用更新**
7
8
 
8
9
  ## 安装
9
10
 
10
11
  ```bash
11
- # 从源码仓库安装(复制即可)
12
- dsh plugin --profile web add github:19952730389/dsh-app-updater
12
+ # npm 包(推荐,插件中心同样可一键安装)
13
+ dsh plugin --profile web add dsh-app-updater
13
14
  ```
14
15
 
15
- 发行 `.tgz` 见 [Releases](https://github.com/19952730389/dsh-app-updater/releases):
16
+ 不想走 npm,也可以用源码或发行包:
16
17
 
17
18
  ```bash
19
+ # 源码仓库
20
+ dsh plugin --profile web add github:19952730389/dsh-app-updater
21
+
22
+ # 预构建 tgz(见 Releases)
18
23
  dsh plugin --profile web add https://github.com/19952730389/dsh-app-updater/releases/latest/download/dsh-app-updater.tgz
19
24
  ```
20
25
 
21
26
  装好后重启 DSH,在 **设置 → 应用更新** 使用。桌面端(Electron 托管的 `desktop` profile)不允许
22
27
  `dsh plugin` 直接改写,需在 profile 的 `node_modules` 下建 junction 并手动补 `bundles`。
23
28
 
24
- ![version](https://img.shields.io/badge/version-1.0.0-4b6fff?style=flat-square)
29
+ [![npm](https://img.shields.io/npm/v/dsh-app-updater?style=flat-square&label=npm&color=0891b2&logo=npm)](https://www.npmjs.com/package/dsh-app-updater)
25
30
  ![license](https://img.shields.io/badge/license-MIT-2f6f4f?style=flat-square)
26
31
  ![platform](https://img.shields.io/badge/platform-Windows-7da1de?style=flat-square)
27
32
  ![dsh-plugin](https://img.shields.io/badge/DSH-plugin-263146?style=flat-square)
@@ -91,6 +96,7 @@ dsh plugin --profile web add https://github.com/19952730389/dsh-app-updater/rele
91
96
  | `keepInstallers` | `true` | 更新后保留安装包;`false` 则在安装完成后删掉该应用刚下载的包。 |
92
97
  | `hashFallbackUpgrade` | `true` | 下载失败时退回 `winget upgrade` 兜底。 |
93
98
  | `dryRun` | `false` | **演练模式**:只下载官网全量安装包、不执行安装。用来先看会装什么。 |
99
+ | `matchSystemArch` | `true` | 按识别到的系统与芯片架构挑选安装包(Windows 传 `--architecture`;macOS 由 Homebrew 决定)。 |
94
100
 
95
101
  用 API 改设置(等价于设置面板,适合脚本化):
96
102
 
@@ -118,7 +124,8 @@ curl -X POST -H "Content-Type: application/json" \
118
124
 
119
125
  ## 依赖
120
126
 
121
- - **Windows**(`winget.exe`,即 Microsoft「应用安装程序 / App Installer」)。没装 winget 时插件仍能列出注册表里的应用,但无法判断是否有新版本。
127
+ - **Windows**:`winget.exe`(Microsoft「应用安装程序 / App Installer」)。没装 winget 时插件仍能列出注册表里的应用,但无法判断是否有新版本。
128
+ - **macOS**:`brew`([Homebrew](https://brew.sh))。没装 Homebrew 时明确报告引擎不可用,不会假装能更新。
122
129
  - 不需要任何第三方 npm 依赖;宿主半身只用 Node 内置模块,浏览器半身只用宿主提供的 `react`。
123
130
 
124
131
  ## 接口(宿主半身)
@@ -127,7 +134,7 @@ curl -X POST -H "Content-Type: application/json" \
127
134
  |---|---|---|
128
135
  | GET | `/dsh-app-updater/state.json` | 当前状态、应用清单、日志、设置 |
129
136
  | GET | `/dsh-app-updater/apps.json` | 只取应用清单与统计 |
130
- | GET | `/dsh-app-updater/health.json` | 自检(路由数、数据目录) |
137
+ | GET | `/dsh-app-updater/health.json` | 自检:版本、运行环境、引擎与能力、路由数、数据目录 |
131
138
  | POST | `/dsh-app-updater/scan` | `{confirm:true, force:true}` 开始扫描 |
132
139
  | POST | `/dsh-app-updater/update` | `{confirm:true, ids:[...]}` 开始更新队列 |
133
140
  | POST | `/dsh-app-updater/cancel` | `{confirm:true}` 取消未开始项 |
@@ -153,11 +160,30 @@ dsh plugin --profile <profile> add link:/path/to/dsh-app-updater
153
160
  ## 测试
154
161
 
155
162
  ```bash
156
- node test/unit.mjs # 36 项:定宽表格解析、版本比较、安装包清单与静默参数
163
+ node test/unit.mjs # 66 项:定宽表格解析、版本比较、清单与静默参数、系统/架构识别、Homebrew 解析
157
164
  node test/render.mjs # 32 项:设置面板、演练提示、列宽拖拽、请求体
158
165
  node test/harness.mjs --scan # 用假 cordis ctx + 真 HTTP 把宿主半身跑起来
159
166
  ```
160
167
 
168
+ ## 支持的系统与架构
169
+
170
+ 插件启动时会识别本机环境,并据此挑选对应架构的安装包:
171
+
172
+ | 系统 | 架构 | 更新引擎 | 说明 |
173
+ |---|---|---|---|
174
+ | Windows 10 / 11 | x64 / ARM64 / x86 | **winget** | 把识别到的架构传给 `winget --architecture`;清单没有该架构的包时自动退回自动匹配并记录日志 |
175
+ | macOS 11+ | Apple Silicon / Intel | **Homebrew cask** | Homebrew 自己按本机架构取 cask;没有 Homebrew 时明确报告引擎不可用 |
176
+ | 其它 | — | 无 | 扫描/更新接口返回 400 并说明原因 |
177
+
178
+ 识别结果会显示在状态行与设置面板的「运行环境」里,例如
179
+ `Windows 11(build 26300) · x64(AMD64) · winget 1.29.380` 或
180
+ `macOS 15(Sequoia) · Apple Silicon(ARM64) · Homebrew 4.5.0`。
181
+
182
+ 还可以在设置面板里关掉「按本机架构安装包」——关闭后把选择权交回 winget 自己(Homebrew 一直按本机架构)。
183
+
184
+ > macOS 的 Homebrew 路径是按 Homebrew 官方 CLI 行为实现的,识别与解析逻辑有单测覆盖;
185
+ > 但本插件主要在 Windows 上开发,macOS 上的安装/更新没有真机验证过,遇到问题请开 Issue。
186
+
161
187
  ## 卸载
162
188
 
163
189
  1. 从 profile 的 `package.json` 里删掉 `dsh.profile.bundles` 中的 `dsh-app-updater` 与 `dependencies` 中的对应条目。
package/RELEASING.md ADDED
@@ -0,0 +1,63 @@
1
+ # 发版流程
2
+
3
+ 这个仓库同时是 **GitHub 插件源码** 和 **npm 包 `dsh-app-updater`**,两者版本必须一致。
4
+
5
+ ## 1. 改版本号与更新日志
6
+
7
+ ```bash
8
+ # package.json 的 version 改成新版本,例如 1.0.1
9
+ # CHANGELOG.md 顶部加一节(遵循 keep-a-changelog 的写法)
10
+ ```
11
+
12
+ ## 2. 本地自测
13
+
14
+ ```bash
15
+ npm test # unit 36 项 + render 32 项
16
+ node test/harness.mjs --scan # 宿主半身跑真实 HTTP(需要 Windows + winget)
17
+ ```
18
+
19
+ ## 3. 发布到 npm
20
+
21
+ npm 目前要求发布必须通过 2FA,或使用**开启 Bypass 2FA 的 Granular Access Token**:
22
+
23
+ ```bash
24
+ # 方式 A:账号已开 2FA
25
+ npm publish --access public --otp=<6 位动态码>
26
+
27
+ # 方式 B:用带 Bypass 2FA 的 token(写入临时 userconfig,别写进仓库)
28
+ npm publish --access public --userconfig=<某个仓库外的 .npmrc>
29
+ ```
30
+
31
+ > 注意:带 Bypass 2FA 的令牌可以做发布,但**不能**做 `npm unpublish` / 账号类操作,
32
+ > 那些仍然需要在网页上以 2FA 完成。
33
+
34
+ ## 4. 打 GitHub Release(附预构建包)
35
+
36
+ ```bash
37
+ npm pack --silent # 产出 dsh-app-updater-<version>.tgz
38
+ mv dsh-app-updater-*.tgz dsh-app-updater.tgz # 资产名不要带版本号
39
+ gh release create v<version> dsh-app-updater.tgz \
40
+ --title "v<version>" --notes-file <(sed -n '/^## \[<version>\]/,/^## \[/p' CHANGELOG.md)
41
+ ```
42
+
43
+ 资产名必须**不带版本号**:README 与各插件市场用的是
44
+ `releases/latest/download/dsh-app-updater.tgz`,`latest/download/` 只解析 `latest`,
45
+ 文件名是按字面取的——一旦资产名带版本号,下一次发版这个链接就会 404。
46
+ 如果确实想让文件名带版本号,就把链接改成钉住 tag 的形式(`releases/download/v1.0.0/…`)。
47
+
48
+ ## 5. 市场收录怎么更新
49
+
50
+ - **dsh-plugin.org / dshopen.org / awesome-dsh-plugin**:仓库更新后它们按各自刷新周期同步,
51
+ 一般不需要重新提交;条目信息(版本、Star、更新时间)会自动跟上。
52
+ - **dsh.works / DSH.SO / DSH STORE**:全部自动抓取,无需任何操作。
53
+ - **DSH Plugin Index(realoiyo.github.io)**:只抓 npm 包,`npm publish` 后下次重建即更新。
54
+ - 提交入口(首次收录用):
55
+ - dsh-plugin.org → https://github.com/dshplugin/dsh-plugin-hub/issues (模板见其 submit 页)
56
+ - dshopen.org → https://github.com/polang233/dsh-plugin-index/issues/new?template=plugin-add.yml
57
+ - awesome-dsh-plugin → 向仓库提 PR,新增 `data/plugins/<owner>__<repo>.yml`(见其 contributing.md)
58
+
59
+ ## 6. 本地开发时的桌面端安装
60
+
61
+ Electron 托管的 `desktop` profile 不接受 `dsh plugin` 写入。做法是在
62
+ `<profile>/node_modules` 下建立指向本仓库的目录 junction,并把包名加进 profile
63
+ `package.json` 的 `dsh.profile.bundles` 与 `dependencies`,重启 DSH 生效。
package/lib/client.js CHANGED
@@ -47,6 +47,11 @@ window.__ModuleLoader__.load({
47
47
  hint: "只从官网下载完整安装包,不执行安装",
48
48
  warn: true,
49
49
  },
50
+ {
51
+ key: "matchSystemArch",
52
+ label: "按本机架构安装包",
53
+ hint: "按当前系统与芯片(Windows ARM64 / x64、Apple Silicon / Intel)挑选对应安装包",
54
+ },
50
55
  {
51
56
  key: "hashFallbackUpgrade",
52
57
  label: "下载失败时回退 winget",
@@ -76,6 +81,14 @@ window.__ModuleLoader__.load({
76
81
  winget: "winget",
77
82
  msstore: "msstore",
78
83
  registry: "registry",
84
+ homebrew: "homebrew",
85
+ };
86
+
87
+ /** 引擎显示名 */
88
+ const ENGINE_TEXT = {
89
+ winget: "winget",
90
+ homebrew: "Homebrew",
91
+ none: "无可用更新引擎",
79
92
  };
80
93
 
81
94
  /* ------------------------------------------------------------------ *
@@ -151,6 +164,8 @@ window.__ModuleLoader__.load({
151
164
  ".appupd-badge-winget{border-color:rgba(79,140,255,.55);color:var(--dsw-alias-brand-primary,#4f8cff);}",
152
165
  ".appupd-badge-msstore{border-color:rgba(52,211,153,.55);color:#34d399;}",
153
166
  ".appupd-badge-registry{border-color:rgba(148,163,184,.5);}",
167
+ ".appupd-badge-homebrew{border-color:rgba(251,191,36,.55);color:#fbbf24;}",
168
+ ".appupd-envline{font-size:12px;color:var(--dsw-alias-label-primary,#f3f4f6);word-break:break-word;}",
154
169
  ".appupd-state-new{font-weight:600;color:var(--dsw-alias-brand-primary,#4f8cff);white-space:nowrap;}",
155
170
  ".appupd-state-ok{color:#34d399;white-space:nowrap;}",
156
171
  ".appupd-state-none{color:var(--dsw-alias-label-secondary,#9ca3af);white-space:nowrap;}",
@@ -267,6 +282,14 @@ window.__ModuleLoader__.load({
267
282
  return REASON_TEXT[r] || String(r);
268
283
  }
269
284
 
285
+ /** 引擎状态文案:winget 1.29.380 / Homebrew 4.5.0 / 无可用更新引擎 */
286
+ function engineTextOf(engine) {
287
+ if (!engine || !engine.id) return "引擎状态未知";
288
+ const name = ENGINE_TEXT[engine.id] || engine.id;
289
+ if (engine.available === false) return name + " 不可用";
290
+ return engine.version ? name + " " + engine.version : name + " 可用";
291
+ }
292
+
270
293
  /* ---------------- 本地偏好(列宽 / 面板展开) ---------------- */
271
294
 
272
295
  /** localStorage 包装:不可用(隐私模式、无 window)时静默降级为默认值。 */
@@ -596,6 +619,15 @@ window.__ModuleLoader__.load({
596
619
  const winget = (state && state.winget) || null;
597
620
  const platform = state && state.platform;
598
621
 
622
+ /* 运行环境(系统 / 架构)与更新引擎 */
623
+ const env = (state && state.env) || null;
624
+ const engine = (state && state.engine) || null;
625
+ const caps = (state && state.capabilities) || null;
626
+ const envSummary =
627
+ (state && state.envSummary) ||
628
+ (env ? [env.osName || env.platform, env.archLabel].filter(Boolean).join(" · ") : "环境未知");
629
+ const engineText = !state ? "" : engineTextOf(engine);
630
+
599
631
  const filtered = react.useMemo(() => {
600
632
  if (!items) return [];
601
633
  const q = query.trim().toLowerCase();
@@ -922,6 +954,30 @@ window.__ModuleLoader__.load({
922
954
  ? h(
923
955
  "div",
924
956
  { className: "appupd-panel-body" },
957
+ h(
958
+ "div",
959
+ { className: "appupd-field" },
960
+ h("div", { className: "appupd-field-label" }, "运行环境"),
961
+ h(
962
+ "div",
963
+ { className: "appupd-field-main" },
964
+ h(
965
+ "div",
966
+ { className: "appupd-field-row" },
967
+ h("span", { className: "appupd-envline" }, envSummary + (engineText ? " · " + engineText : ""))
968
+ ),
969
+ env && env.cpuModel
970
+ ? h(
971
+ "div",
972
+ { className: "appupd-hint" },
973
+ env.cpuModel + (env.cpuCount ? " · " + env.cpuCount + " 线程" : "")
974
+ )
975
+ : null
976
+ )
977
+ ),
978
+ caps && !caps.scan && caps.note
979
+ ? h("div", { className: "appupd-err", style: { fontSize: 12 } }, caps.note)
980
+ : null,
925
981
  h(
926
982
  "div",
927
983
  { className: "appupd-field" },
@@ -1039,16 +1095,18 @@ window.__ModuleLoader__.load({
1039
1095
  ? h("span", null, loadError ? h("span", { className: "appupd-err" }, "无法连接宿主:" + loadError) : "正在读取状态…")
1040
1096
  : h(
1041
1097
  "span",
1042
- null,
1043
- winget && winget.available === false
1044
- ? h("span", { className: "appupd-err" }, "未检测到 winget,无法检测更新")
1045
- : h(
1046
- "span",
1047
- { className: "appupd-status-strong" },
1048
- winget && winget.version ? "winget " + winget.version : winget && winget.available ? "winget 可用" : "winget 状态未知"
1049
- )
1098
+ engine && engine.available === false ? { className: "appupd-err" } : null,
1099
+ engineText
1050
1100
  ),
1051
1101
  state ? h("span", { className: "appupd-sep" }, "·") : null,
1102
+ state
1103
+ ? h(
1104
+ "span",
1105
+ { className: "appupd-status-strong", title: "操作系统与 CPU 架构" },
1106
+ envSummary
1107
+ )
1108
+ : null,
1109
+ state ? h("span", { className: "appupd-sep" }, "·") : null,
1052
1110
  state
1053
1111
  ? h("span", null, "上次扫描:" + fmtRel(apps && apps.scannedAt, now))
1054
1112
  : null,
@@ -1061,7 +1119,7 @@ window.__ModuleLoader__.load({
1061
1119
  )
1062
1120
  : null,
1063
1121
  scanning ? h("span", null, "扫描中…") : null,
1064
- platform && platform !== "win32" ? h("span", { className: "appupd-err" }, "仅支持 Windows") : null,
1122
+ caps && caps.note ? h("span", { className: "appupd-err" }, caps.note) : null,
1065
1123
  loadError && state ? h("span", { className: "appupd-err" }, "(状态刷新失败:" + loadError + ")") : null
1066
1124
  );
1067
1125