dshmarket 1.31.2 → 1.33.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/README.md +1 -0
- package/README.zh.md +1 -0
- package/UPDATE-API-V1.md +115 -0
- package/client/client.js +24 -2
- package/client/client.js.map +1 -1
- package/lib/check.js +28 -5
- package/lib/pnpm-compat.js +52 -0
- package/lib/routes.js +282 -6
- package/lib/types/pnpm-compat.d.ts +1 -1
- package/lib/types/update-api-v1.d.ts +66 -0
- package/lib/update-api-v1.js +207 -0
- package/package.json +6 -1
- package/src/check.ts +32 -6
- package/src/client/MarketSection.tsx +21 -2
- package/src/pnpm-compat.ts +50 -1
- package/src/routes.ts +286 -6
- package/src/update-api-v1.ts +266 -0
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@ its own dsh: it may be older than the one `npm` would give you (#139).
|
|
|
37
37
|
- **One-click install** — confirm the source, watch live progress; most plugins go live after a page refresh, no restart
|
|
38
38
|
- **Backup & restore** — export your profile's plugin list and configuration as readable JSON, import it on another machine, store it on WebDAV with daily auto-backup, or sync through a private GitHub Gist; restores **merge** (plugins installed after the backup are kept), validate before writing, and roll back on failure
|
|
39
39
|
- **Updates** — per-plugin update checks (npm version or pinned commit vs HEAD), one-click update, or update everything at once; the market updates itself the same way
|
|
40
|
+
- **Public update API** — plugin-owned settings pages can use the versioned, capability-gated [update API v1](UPDATE-API-V1.md) (beta) instead of copying package-manager logic or depending on private Market UI responses
|
|
40
41
|
- **Uninstall** — two-step confirm; plugins installed this session are removed live
|
|
41
42
|
- **Hot disable / enable** — toggles write `- id: …` + `disabled: true|false` into the profile's `cordis.patch.yml` (the official patch layer, mechanism ported from [dsh-plugin-hub](https://github.com/Noob-stupid/dsh-plugin-hub)): DSH's HMR re-composes within ~1s, no restart, and the loader re-applies the choice on every boot; hand-edited patch rows show as badges, host-infrastructure plugins are protected from toggling, and a malformed patch file is never made worse
|
|
42
43
|
- **Restart when needed** — changes that cannot hot-load show a one-click restart beside the pending-change banner; the action is restricted to same-origin loopback requests
|
package/README.zh.md
CHANGED
|
@@ -35,6 +35,7 @@ dsh plugin --profile web add dshmarket
|
|
|
35
35
|
- **一键安装**——确认来源,实时进度;多数插件刷新页面即可用,无需重启
|
|
36
36
|
- **备份与恢复**——把 profile 的插件清单与配置导出为可读 JSON,换机导入,存到 WebDAV 并每日自动备份,或通过私有 GitHub Gist 跨机器同步;恢复采用**合并**方式(备份之后新装的插件会保留),写入前校验、失败自动回滚
|
|
37
37
|
- **更新**——逐插件检测(npm 版本或锁定 commit 对比 HEAD),一键更新或全部更新;市场自己也走同一通道升级
|
|
38
|
+
- **公共更新接口**——插件自己的设置页可调用带版本号、能力探测和回滚状态的[更新 API v1](UPDATE-API-V1.md)(beta),无需复制包管理逻辑,也不依赖市场 UI 的私有响应字段
|
|
38
39
|
- **卸载**——两步确认防误触;本次会话装的插件即点即卸
|
|
39
40
|
- **热禁用 / 启用**——开关会往 profile 的 `cordis.patch.yml`(官方补丁层,机制移植自 [dsh-plugin-hub](https://github.com/Noob-stupid/dsh-plugin-hub))写入 `- id: …` + `disabled: true|false`:DSH 的 HMR 约 1 秒内重新组合,无需重启,loader 每次启动都会重新应用这个选择;手工改过的补丁行会显示成徽标,宿主基础设施插件禁止开关,补丁文件格式不对时绝不会被写得更糟
|
|
40
41
|
- **按需重启**——无法热加载的变更会在待重启提示旁显示一键重启;操作仅接受本机同源请求
|
package/UPDATE-API-V1.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Public plugin update API v1
|
|
2
|
+
|
|
3
|
+
> **Status: beta.** The shape described here may still change between
|
|
4
|
+
> releases. `GET /dsh-market/api/v1/capabilities` reports
|
|
5
|
+
> `"stability": "beta"` while that is true, and `"stable"` once it stops
|
|
6
|
+
> moving — read that field rather than assuming from the `v1` in the path.
|
|
7
|
+
>
|
|
8
|
+
> Nothing here is going away; what is not yet promised is that field names
|
|
9
|
+
> and response shapes will survive untouched. If you ship against it now,
|
|
10
|
+
> say so in an issue: a shape somebody depends on is a much stronger reason
|
|
11
|
+
> not to move it, and it is how this reaches `stable`.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
`dshmarket` exposes a small, versioned, same-origin JSON API for plugin-owned
|
|
15
|
+
update surfaces. It lets a plugin show its own update button without spawning a
|
|
16
|
+
package manager, copying the Market installation algorithm, or binding to the
|
|
17
|
+
Market UI's private response fields.
|
|
18
|
+
|
|
19
|
+
All responses carry:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{ "schema": "dsh-market/update-api/v1" }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Clients must discover the API before enabling mutation controls:
|
|
26
|
+
|
|
27
|
+
```http
|
|
28
|
+
GET /dsh-market/api/v1/capabilities
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The response names the Market version, profile, runtime (`web` or `desktop`),
|
|
32
|
+
supported features, restart owner and endpoint paths. A client must hide its
|
|
33
|
+
restart button when `restart.supported` is false. Desktop and supervised hosts
|
|
34
|
+
normally delegate restart to their owning shell or operator.
|
|
35
|
+
|
|
36
|
+
## Check one installed package
|
|
37
|
+
|
|
38
|
+
```http
|
|
39
|
+
GET /dsh-market/api/v1/updates?name=dsh-mcp-connector&force=1
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The response includes the installed version, target version, source kind and
|
|
43
|
+
whether the target is a forward update. Omitting `force=1` allows the Market's
|
|
44
|
+
short update-check cache.
|
|
45
|
+
|
|
46
|
+
## Start and observe an update
|
|
47
|
+
|
|
48
|
+
Mutation requests require the same-origin protection used by the Market UI.
|
|
49
|
+
|
|
50
|
+
```http
|
|
51
|
+
POST /dsh-market/api/v1/updates
|
|
52
|
+
Content-Type: application/json
|
|
53
|
+
|
|
54
|
+
{ "packageName": "dsh-mcp-connector" }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
An accepted request returns HTTP `202` immediately with an `operationId`.
|
|
58
|
+
Passing `"force": true` opts this one operation out of the registry release-age
|
|
59
|
+
wait; clients should offer it only after the normal operation reports
|
|
60
|
+
`RELEASE_TOO_FRESH` or `VERSION_UNCHANGED`.
|
|
61
|
+
|
|
62
|
+
Poll the operation by id:
|
|
63
|
+
|
|
64
|
+
```http
|
|
65
|
+
GET /dsh-market/api/v1/operations?operationId=<id>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
States are `queued`, `running`, `succeeded`, `failed`, `cancelled` and
|
|
69
|
+
`rolled-back`. Running operations include structured package progress when
|
|
70
|
+
pnpm provides it. Terminal operations include:
|
|
71
|
+
|
|
72
|
+
- the before and actually installed versions;
|
|
73
|
+
- `refreshRequired` and `restartRequired` outcomes;
|
|
74
|
+
- a stable failure code, bounded user-facing message and retryability;
|
|
75
|
+
- whether a compatibility rollback is currently available.
|
|
76
|
+
|
|
77
|
+
Up to 50 operation records live in the current Host process. A boot id is
|
|
78
|
+
embedded in every operation id, so a client never mistakes a stale browser
|
|
79
|
+
record for a task belonging to the replacement process.
|
|
80
|
+
|
|
81
|
+
## Roll back
|
|
82
|
+
|
|
83
|
+
```http
|
|
84
|
+
POST /dsh-market/api/v1/rollback
|
|
85
|
+
Content-Type: application/json
|
|
86
|
+
|
|
87
|
+
{ "operationId": "<id>" }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Rollback is intentionally capability- and operation-scoped. It is available
|
|
91
|
+
only when the Market's compatibility verification retained a recovery point;
|
|
92
|
+
a later mutation may supersede it. The normalized result is written back to
|
|
93
|
+
the same operation record.
|
|
94
|
+
|
|
95
|
+
## Restart
|
|
96
|
+
|
|
97
|
+
```http
|
|
98
|
+
POST /dsh-market/api/v1/restart
|
|
99
|
+
Content-Type: application/json
|
|
100
|
+
|
|
101
|
+
{}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This preserves the Market's stricter restart guard: direct loopback,
|
|
105
|
+
same-origin, no forwarding headers, no package mutation in progress, and a Host
|
|
106
|
+
whose lifecycle is not owned by Desktop or a supervisor. Clients must feature
|
|
107
|
+
detect it; they must not invent an alternative process-control path.
|
|
108
|
+
|
|
109
|
+
## Compatibility policy
|
|
110
|
+
|
|
111
|
+
- New optional response fields may be added within v1.
|
|
112
|
+
- Existing v1 fields and meanings are not repurposed.
|
|
113
|
+
- A breaking change uses a new path and schema version.
|
|
114
|
+
- When discovery is unavailable, plugin UIs should fall back to opening the
|
|
115
|
+
Market rather than calling legacy `/dsh-market/*` mutation routes directly.
|
package/client/client.js
CHANGED
|
@@ -4428,12 +4428,16 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
4428
4428
|
* clock instead of letting it fire again moments later: without that, a
|
|
4429
4429
|
* deliberate "go back one" reads as broken when it auto-advances right past
|
|
4430
4430
|
* where the user just navigated to.
|
|
4431
|
+
*
|
|
4432
|
+
* `intervalMs <= 0` disables the timer entirely (no auto-advance at all);
|
|
4433
|
+
* manual jumps still work. The lightbox uses this: a full-bleed image needs
|
|
4434
|
+
* to stay put until the viewer moves on, so it must never page itself.
|
|
4431
4435
|
*/
|
|
4432
4436
|
function useAutoCarousel(count, initial, intervalMs = 3500) {
|
|
4433
4437
|
const [index, setIndexState] = (0, react.useState)(initial);
|
|
4434
4438
|
const [resetTick, setResetTick] = (0, react.useState)(0);
|
|
4435
4439
|
(0, react.useEffect)(() => {
|
|
4436
|
-
if (count <= 1) return;
|
|
4440
|
+
if (count <= 1 || intervalMs <= 0) return;
|
|
4437
4441
|
const timer = setInterval(() => {
|
|
4438
4442
|
setIndexState((i) => (i + 1) % count);
|
|
4439
4443
|
}, intervalMs);
|
|
@@ -4779,7 +4783,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
4779
4783
|
* vs "full size" asset to fetch.
|
|
4780
4784
|
*/
|
|
4781
4785
|
function ScreenshotLightbox({ shots, startIndex, onClose, t }) {
|
|
4782
|
-
const [index, setIndex] = useAutoCarousel(shots.length, startIndex,
|
|
4786
|
+
const [index, setIndex] = useAutoCarousel(shots.length, startIndex, 0);
|
|
4783
4787
|
(0, react.useEffect)(() => {
|
|
4784
4788
|
const onKey = (e) => {
|
|
4785
4789
|
if (e.key === "Escape") {
|
|
@@ -5610,6 +5614,24 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
5610
5614
|
else el.scrollTop = 0;
|
|
5611
5615
|
}
|
|
5612
5616
|
};
|
|
5617
|
+
(0, react.useLayoutEffect)(() => {
|
|
5618
|
+
const el = bodyRef.current;
|
|
5619
|
+
if (el !== null) el.scrollTop = 0;
|
|
5620
|
+
setShowTop(false);
|
|
5621
|
+
}, [
|
|
5622
|
+
tab,
|
|
5623
|
+
q,
|
|
5624
|
+
cat,
|
|
5625
|
+
sortField,
|
|
5626
|
+
sortDir,
|
|
5627
|
+
timeRange,
|
|
5628
|
+
qThemes,
|
|
5629
|
+
themeSortField,
|
|
5630
|
+
themeSortDir,
|
|
5631
|
+
themeTimeRange,
|
|
5632
|
+
qInstalled,
|
|
5633
|
+
installedView
|
|
5634
|
+
]);
|
|
5613
5635
|
const plugins = (0, react.useMemo)(() => data === null ? [] : visiblePlugins(data.plugins, {
|
|
5614
5636
|
category: cat,
|
|
5615
5637
|
query: q,
|