dshmarket 1.46.0 → 1.46.1
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/lib/routes.js +42 -6
- package/package.json +1 -1
- package/src/routes.ts +41 -6
package/lib/routes.js
CHANGED
|
@@ -433,14 +433,31 @@ export function mountMarketRoutes(host, config, commandRuntime, agentsLookup) {
|
|
|
433
433
|
return { changed: [...changed], partial: changed.size > 0 };
|
|
434
434
|
}
|
|
435
435
|
/**
|
|
436
|
-
* Apply one enable/disable request:
|
|
437
|
-
*
|
|
438
|
-
* client-only shims go through hotUnmount/hotMount, bundle-layer
|
|
439
|
-
* through setEntryDisabled. Enabling a THEME goes through the
|
|
440
|
-
* activateTheme instead so the Themes tab's exclusivity stays
|
|
436
|
+
* Apply one enable/disable request: drive the live composition, then
|
|
437
|
+
* persist the choice in state.json. Covers every mount form — hot mounts
|
|
438
|
+
* and client-only shims go through hotUnmount/hotMount, bundle-layer
|
|
439
|
+
* entries through setEntryDisabled. Enabling a THEME goes through the
|
|
440
|
+
* caller's activateTheme instead so the Themes tab's exclusivity stays
|
|
441
|
+
* intact.
|
|
442
|
+
*
|
|
443
|
+
* A FAILED ENABLE LEAVES EVERYTHING AS IT WAS (#575). The choice used to be
|
|
444
|
+
* recorded before the mount was attempted and persisted whatever happened,
|
|
445
|
+
* so enabling a plugin that crashes on import — deterministically, every
|
|
446
|
+
* time — wrote "enabled" into state.json anyway. The next boot tried the
|
|
447
|
+
* import again and died again; the reporter measured 24 restarts before
|
|
448
|
+
* restoring the disable by hand. The toggle route's patch-layer gate
|
|
449
|
+
* (@JINITAIMEI121 in #584) closed the same hole in cordis.patch.yml; this
|
|
450
|
+
* closes it in the market's own store, which is the ONLY durable state a
|
|
451
|
+
* client-only plugin has — that plugin kind has no bundle rows, so the
|
|
452
|
+
* patch gate never runs for it.
|
|
453
|
+
*
|
|
454
|
+
* A failed DISABLE still persists, and that asymmetry is deliberate: the
|
|
455
|
+
* user asked for OFF, and a failed unmount leaves the plugin live only for
|
|
456
|
+
* this session. There the durable disable is the contract, not an error.
|
|
441
457
|
*/
|
|
442
458
|
async function setPluginEnabled(name, enabled) {
|
|
443
459
|
const dir = activeProfileDir;
|
|
460
|
+
const wasDisabled = disabled.has(name);
|
|
444
461
|
if (enabled)
|
|
445
462
|
disabled.delete(name);
|
|
446
463
|
else
|
|
@@ -474,6 +491,14 @@ export function mountMarketRoutes(host, config, commandRuntime, agentsLookup) {
|
|
|
474
491
|
ok = true;
|
|
475
492
|
}
|
|
476
493
|
}
|
|
494
|
+
if (!ok && enabled) {
|
|
495
|
+
// Put the in-memory view back before persisting: it is the same object
|
|
496
|
+
// the route reports as `disabled`, so restoring it keeps the reply, the
|
|
497
|
+
// store and the patch layer telling one story.
|
|
498
|
+
if (wasDisabled)
|
|
499
|
+
disabled.add(name);
|
|
500
|
+
logEvent('warn', 'toggle', `${name}: enable failed; leaving it disabled rather than persisting a state that crashes at boot (#575)`);
|
|
501
|
+
}
|
|
477
502
|
writeMarketState(dir, { disabled, groups, groupOrder });
|
|
478
503
|
return { ok, reason };
|
|
479
504
|
}
|
|
@@ -2004,7 +2029,18 @@ export function mountMarketRoutes(host, config, commandRuntime, agentsLookup) {
|
|
|
2004
2029
|
}
|
|
2005
2030
|
}
|
|
2006
2031
|
let patchWrite = null;
|
|
2007
|
-
|
|
2032
|
+
// #575: a failed ENABLE must not flip the durable patch layer.
|
|
2033
|
+
// The hot-mount failure may be deterministic (a plugin that
|
|
2034
|
+
// crashes on import), and persisting "enabled" turns a transient
|
|
2035
|
+
// in-session error into a boot crash loop — the loader re-applies
|
|
2036
|
+
// the flipped rows on every start. The frontend already shows the
|
|
2037
|
+
// plugin as still disabled, and the next explicit enable retries
|
|
2038
|
+
// cleanly. Disables keep their unconditional write: a failed
|
|
2039
|
+
// unmount leaves the plugin live in-session, and the user asked
|
|
2040
|
+
// for it OFF — the durable disable is then the contract, not an
|
|
2041
|
+
// error.
|
|
2042
|
+
const patchGate = ok || !enabled;
|
|
2043
|
+
if (patchRows.length > 0 && patchGate) {
|
|
2008
2044
|
for (const rowId of patchRows) {
|
|
2009
2045
|
const result = enabled ? await enableRow(userPatchPath, rowId) : await disableRow(userPatchPath, rowId);
|
|
2010
2046
|
if (!result.ok && patchWrite === null)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dshmarket",
|
|
3
3
|
"description": "Visual plugin market inside DeepSeek Harness — browse, search, and one-click install community plugins. · DSH 可视化插件市场:逛一逛,点一下,装好。",
|
|
4
|
-
"version": "1.46.
|
|
4
|
+
"version": "1.46.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
package/src/routes.ts
CHANGED
|
@@ -487,14 +487,31 @@ export function mountMarketRoutes(
|
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
/**
|
|
490
|
-
* Apply one enable/disable request:
|
|
491
|
-
*
|
|
492
|
-
* client-only shims go through hotUnmount/hotMount, bundle-layer
|
|
493
|
-
* through setEntryDisabled. Enabling a THEME goes through the
|
|
494
|
-
* activateTheme instead so the Themes tab's exclusivity stays
|
|
490
|
+
* Apply one enable/disable request: drive the live composition, then
|
|
491
|
+
* persist the choice in state.json. Covers every mount form — hot mounts
|
|
492
|
+
* and client-only shims go through hotUnmount/hotMount, bundle-layer
|
|
493
|
+
* entries through setEntryDisabled. Enabling a THEME goes through the
|
|
494
|
+
* caller's activateTheme instead so the Themes tab's exclusivity stays
|
|
495
|
+
* intact.
|
|
496
|
+
*
|
|
497
|
+
* A FAILED ENABLE LEAVES EVERYTHING AS IT WAS (#575). The choice used to be
|
|
498
|
+
* recorded before the mount was attempted and persisted whatever happened,
|
|
499
|
+
* so enabling a plugin that crashes on import — deterministically, every
|
|
500
|
+
* time — wrote "enabled" into state.json anyway. The next boot tried the
|
|
501
|
+
* import again and died again; the reporter measured 24 restarts before
|
|
502
|
+
* restoring the disable by hand. The toggle route's patch-layer gate
|
|
503
|
+
* (@JINITAIMEI121 in #584) closed the same hole in cordis.patch.yml; this
|
|
504
|
+
* closes it in the market's own store, which is the ONLY durable state a
|
|
505
|
+
* client-only plugin has — that plugin kind has no bundle rows, so the
|
|
506
|
+
* patch gate never runs for it.
|
|
507
|
+
*
|
|
508
|
+
* A failed DISABLE still persists, and that asymmetry is deliberate: the
|
|
509
|
+
* user asked for OFF, and a failed unmount leaves the plugin live only for
|
|
510
|
+
* this session. There the durable disable is the contract, not an error.
|
|
495
511
|
*/
|
|
496
512
|
async function setPluginEnabled(name: string, enabled: boolean): Promise<{ ok: boolean; reason?: string }> {
|
|
497
513
|
const dir = activeProfileDir
|
|
514
|
+
const wasDisabled = disabled.has(name)
|
|
498
515
|
if (enabled) disabled.delete(name)
|
|
499
516
|
else disabled.add(name)
|
|
500
517
|
let ok: boolean
|
|
@@ -522,6 +539,13 @@ export function mountMarketRoutes(
|
|
|
522
539
|
ok = true
|
|
523
540
|
}
|
|
524
541
|
}
|
|
542
|
+
if (!ok && enabled) {
|
|
543
|
+
// Put the in-memory view back before persisting: it is the same object
|
|
544
|
+
// the route reports as `disabled`, so restoring it keeps the reply, the
|
|
545
|
+
// store and the patch layer telling one story.
|
|
546
|
+
if (wasDisabled) disabled.add(name)
|
|
547
|
+
logEvent('warn', 'toggle', `${name}: enable failed; leaving it disabled rather than persisting a state that crashes at boot (#575)`)
|
|
548
|
+
}
|
|
525
549
|
writeMarketState(dir, { disabled, groups, groupOrder })
|
|
526
550
|
return { ok, reason }
|
|
527
551
|
}
|
|
@@ -2109,7 +2133,18 @@ export function mountMarketRoutes(
|
|
|
2109
2133
|
}
|
|
2110
2134
|
}
|
|
2111
2135
|
let patchWrite: { ok: boolean; reason: string | null } | null = null
|
|
2112
|
-
|
|
2136
|
+
// #575: a failed ENABLE must not flip the durable patch layer.
|
|
2137
|
+
// The hot-mount failure may be deterministic (a plugin that
|
|
2138
|
+
// crashes on import), and persisting "enabled" turns a transient
|
|
2139
|
+
// in-session error into a boot crash loop — the loader re-applies
|
|
2140
|
+
// the flipped rows on every start. The frontend already shows the
|
|
2141
|
+
// plugin as still disabled, and the next explicit enable retries
|
|
2142
|
+
// cleanly. Disables keep their unconditional write: a failed
|
|
2143
|
+
// unmount leaves the plugin live in-session, and the user asked
|
|
2144
|
+
// for it OFF — the durable disable is then the contract, not an
|
|
2145
|
+
// error.
|
|
2146
|
+
const patchGate = ok || !enabled
|
|
2147
|
+
if (patchRows.length > 0 && patchGate) {
|
|
2113
2148
|
for (const rowId of patchRows) {
|
|
2114
2149
|
const result = enabled ? await enableRow(userPatchPath, rowId) : await disableRow(userPatchPath, rowId)
|
|
2115
2150
|
if (!result.ok && patchWrite === null) patchWrite = result
|