dshmarket 1.48.0 → 1.49.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/client/client.js +11 -5
- package/lib/dsh-cli.js +28 -1
- package/lib/install.js +12 -0
- package/lib/restart.js +15 -1
- package/lib/types/dsh-cli.d.ts +17 -0
- package/lib/types/install.d.ts +12 -0
- package/lib/types/restart.d.ts +4 -0
- package/package.json +1 -1
- package/src/client/MarketSection.tsx +26 -4
- package/src/client/locales.ts +2 -2
- package/src/dsh-cli.ts +28 -1
- package/src/install.ts +12 -0
- package/src/restart.ts +15 -1
package/client/client.js
CHANGED
|
@@ -105,7 +105,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
105
105
|
installing: "安装中…",
|
|
106
106
|
installedBadge: "✓ 已安装",
|
|
107
107
|
alreadyInstalled: "✓ 已安装",
|
|
108
|
-
restartBanner: "
|
|
108
|
+
restartBanner: "项变更需重启 DeepSeek Harness 后生效",
|
|
109
109
|
uninstall: "卸载",
|
|
110
110
|
confirmRemove: "确认卸载?",
|
|
111
111
|
uninstalling: "卸载中…",
|
|
@@ -638,7 +638,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
638
638
|
installing: "Installing…",
|
|
639
639
|
installedBadge: "✓ Installed",
|
|
640
640
|
alreadyInstalled: "✓ Installed",
|
|
641
|
-
restartBanner: "change(s)
|
|
641
|
+
restartBanner: "change(s) pending restart — restart DeepSeek Harness to apply",
|
|
642
642
|
uninstall: "Uninstall",
|
|
643
643
|
confirmRemove: "Uninstall?",
|
|
644
644
|
uninstalling: "Uninstalling…",
|
|
@@ -6348,6 +6348,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
6348
6348
|
const [buildsSkipped, setBuildsSkipped] = (0, react.useState)(null);
|
|
6349
6349
|
const [updatingAll, setUpdatingAll] = (0, react.useState)(false);
|
|
6350
6350
|
const [updatedNames, setUpdatedNames] = (0, react.useState)([]);
|
|
6351
|
+
const [restartNames, setRestartNames] = (0, react.useState)([]);
|
|
6351
6352
|
const [hotUrls, setHotUrls] = (0, react.useState)([]);
|
|
6352
6353
|
const [hotNames, setHotNames] = (0, react.useState)([]);
|
|
6353
6354
|
const [progressLine, setProgressLine] = (0, react.useState)(null);
|
|
@@ -6667,12 +6668,13 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
6667
6668
|
}
|
|
6668
6669
|
if (Array.isArray(saved.doneUrls) && saved.doneUrls.length > 0) setDoneUrls(saved.doneUrls);
|
|
6669
6670
|
if (Array.isArray(saved.updated) && saved.updated.length > 0) setUpdatedNames(saved.updated);
|
|
6671
|
+
if (Array.isArray(saved.restartNames) && saved.restartNames.length > 0) setRestartNames(saved.restartNames);
|
|
6670
6672
|
if (typeof saved.removed === "number" && saved.removed > 0) setRemovedCount(saved.removed);
|
|
6671
6673
|
if (typeof saved.toggled === "number" && saved.toggled > 0) setToggleRestart(saved.toggled);
|
|
6672
6674
|
}, [bootId]);
|
|
6673
6675
|
(0, react.useEffect)(() => {
|
|
6674
6676
|
if (bootId === null) return;
|
|
6675
|
-
if (doneUrls.length === 0 && updatedNames.length === 0 && removedCount === 0 && toggleRestart === 0) {
|
|
6677
|
+
if (doneUrls.length === 0 && updatedNames.length === 0 && restartNames.length === 0 && removedCount === 0 && toggleRestart === 0) {
|
|
6676
6678
|
sessionStorage.removeItem("dshm-restart");
|
|
6677
6679
|
return;
|
|
6678
6680
|
}
|
|
@@ -6680,6 +6682,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
6680
6682
|
boot: bootId,
|
|
6681
6683
|
doneUrls,
|
|
6682
6684
|
updated: updatedNames,
|
|
6685
|
+
restartNames,
|
|
6683
6686
|
removed: removedCount,
|
|
6684
6687
|
toggled: toggleRestart
|
|
6685
6688
|
}));
|
|
@@ -6687,6 +6690,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
6687
6690
|
bootId,
|
|
6688
6691
|
doneUrls,
|
|
6689
6692
|
updatedNames,
|
|
6693
|
+
restartNames,
|
|
6690
6694
|
removedCount,
|
|
6691
6695
|
toggleRestart
|
|
6692
6696
|
]);
|
|
@@ -7307,6 +7311,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
7307
7311
|
if (status === 200 && body.ok) {
|
|
7308
7312
|
setRecords((list) => patch(list, updateRecordId, { state: "done" }));
|
|
7309
7313
|
setUpdatedNames((names) => names.concat(name));
|
|
7314
|
+
const activation = body.activation && typeof body.activation === "object" ? body.activation[name] : void 0;
|
|
7315
|
+
if (!activation || activation.state === "restart") setRestartNames((names) => names.includes(name) ? names : names.concat(name));
|
|
7310
7316
|
if (body.activation && typeof body.activation === "object") setActivations((prev) => ({
|
|
7311
7317
|
...prev,
|
|
7312
7318
|
...body.activation
|
|
@@ -7854,7 +7860,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
7854
7860
|
});
|
|
7855
7861
|
}, [doGroupAction, groups]);
|
|
7856
7862
|
const selfName = installed["dshmarket"] !== void 0 ? "dshmarket" : "dsh-market";
|
|
7857
|
-
const updatableNames = Object.keys(installed).filter((name) => name !== selfName && !updatedNames.includes(name) && updates[name] && updates[name].updateAvailable);
|
|
7863
|
+
const updatableNames = Object.keys(installed).filter((name) => name !== selfName && !updatedNames.includes(name) && !effectiveDisabledSet.has(name) && updates[name] && updates[name].updateAvailable);
|
|
7858
7864
|
const batchUpdatableNames = updatableNames.filter((name) => updates[name]?.restoreRequired !== true);
|
|
7859
7865
|
const ignoredUpdateSet = (0, react.useMemo)(() => new Set(ignoredUpdateNames), [ignoredUpdateNames]);
|
|
7860
7866
|
const reminderUpdatableNames = updatableNames.filter((name) => !ignoredUpdateSet.has(name));
|
|
@@ -8102,7 +8108,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
8102
8108
|
webdavUrl,
|
|
8103
8109
|
webdavUser
|
|
8104
8110
|
]);
|
|
8105
|
-
const sessionPendingRestart = doneUrls.length +
|
|
8111
|
+
const sessionPendingRestart = doneUrls.length + restartNames.length + removedCount + toggleRestart + (backupRestored ? 1 : 0);
|
|
8106
8112
|
/**
|
|
8107
8113
|
* Plugins the HOST reports as restart-pending, independent of what this
|
|
8108
8114
|
* browser session happens to remember. Installing and then reloading the
|
package/lib/dsh-cli.js
CHANGED
|
@@ -230,6 +230,33 @@ export function gitEnvForPnpm(env = process.env) {
|
|
|
230
230
|
return {};
|
|
231
231
|
return { GIT_TERMINAL_PROMPT: '0' };
|
|
232
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Every `--config.<key>=<value>` override in the argv, repeated as the
|
|
235
|
+
* `PNPM_CONFIG_<KEY>` environment variable that pnpm 12 still reads.
|
|
236
|
+
*
|
|
237
|
+
* pnpm 12 ignores some `--config.<key>` overrides on the command line, in
|
|
238
|
+
* either spelling, without a word: `fetchTimeout` (#615; measured on
|
|
239
|
+
* 12.2.1, 12.3.0 and 12.4.1) and `auto-install-peers` (12.4.1 auto-installs
|
|
240
|
+
* the peer with the flag present and not with the variable), so the retries
|
|
241
|
+
* that carry them ran exactly like the first attempt. `PNPM_CONFIG_*` is
|
|
242
|
+
* honoured by 11.8, 11.21 and 12.4 alike, so the argument stays for the
|
|
243
|
+
* versions that read it and the variable carries the same value for the
|
|
244
|
+
* ones that do not. Scoped to the run that carries the flag; nothing is set
|
|
245
|
+
* otherwise, so a user's own values are untouched on every other run. Keys
|
|
246
|
+
* are accepted in either spelling, so respelling a constant (as #600 did
|
|
247
|
+
* for the release-age one) cannot silently drop the variable.
|
|
248
|
+
*/
|
|
249
|
+
export function pnpmConfigEnvForArgs(pluginArgs) {
|
|
250
|
+
const env = {};
|
|
251
|
+
for (const arg of pluginArgs) {
|
|
252
|
+
const match = /^--config\.([A-Za-z][A-Za-z0-9-]*)=(\S+)$/.exec(arg);
|
|
253
|
+
if (match === null)
|
|
254
|
+
continue;
|
|
255
|
+
const key = match[1].replace(/([a-z0-9])([A-Z])/g, '$1_$2').replace(/-/g, '_').toUpperCase();
|
|
256
|
+
env[`PNPM_CONFIG_${key}`] = match[2];
|
|
257
|
+
}
|
|
258
|
+
return env;
|
|
259
|
+
}
|
|
233
260
|
function spawnEnv() {
|
|
234
261
|
// pnpm v10+ blocks forever on a silent interactive prompt without a TTY;
|
|
235
262
|
// CI mode forces it to act or fail instead of asking.
|
|
@@ -739,7 +766,7 @@ export function runDshPlugin(profile, pluginArgs) {
|
|
|
739
766
|
// pnpm v10 blocks forever on a silent interactive prompt without a TTY
|
|
740
767
|
// (observed on re-add over a pinned git spec); CI mode forces it to act
|
|
741
768
|
// or fail instead of asking.
|
|
742
|
-
env: spawnEnv(),
|
|
769
|
+
env: { ...spawnEnv(), ...pnpmConfigEnvForArgs(pluginArgs) },
|
|
743
770
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
744
771
|
viaShell,
|
|
745
772
|
// Own process group on POSIX so cancel/timeout can kill the whole
|
package/lib/install.js
CHANGED
|
@@ -29,6 +29,14 @@ export const RELEASE_AGE_OVERRIDE = '--config.minimum-release-age=0';
|
|
|
29
29
|
* WHOLE repo even for a `#path:` subdirectory plugin) on slow networks; a
|
|
30
30
|
* plain retry fails again at the same limit, so the recovery re-runs with
|
|
31
31
|
* this override once. Scoped to a single command like RELEASE_AGE_OVERRIDE.
|
|
32
|
+
*
|
|
33
|
+
* pnpm 12 ignores this flag on the command line in either spelling (#615).
|
|
34
|
+
* On the CLI runner, runDshPlugin repeats every `--config.<key>` override
|
|
35
|
+
* as PNPM_CONFIG_<KEY>, which pnpm 11 and 12 both read, so the retried
|
|
36
|
+
* command really does get the longer limit there. The Desktop runtime hands
|
|
37
|
+
* its host argv only, so on a Desktop host the retry still depends on that
|
|
38
|
+
* host's pnpm reading the flag. The flag stays: pnpm 11 and earlier read
|
|
39
|
+
* it, and it costs nothing on the versions that do not.
|
|
32
40
|
*/
|
|
33
41
|
export const FETCH_TIMEOUT_OVERRIDE = '--config.fetchTimeout=600000';
|
|
34
42
|
/**
|
|
@@ -47,6 +55,10 @@ export const FETCH_TIMEOUT_OVERRIDE = '--config.fetchTimeout=600000';
|
|
|
47
55
|
* Verified against pnpm 10.29.3: `peerDependencyRules.ignoreMissing` does
|
|
48
56
|
* NOT prevent the fetch (it only silences the warning), so this flag is the
|
|
49
57
|
* only lever that actually works.
|
|
58
|
+
*
|
|
59
|
+
* pnpm 12 ignores this flag on the command line too (12.4.1 auto-installs
|
|
60
|
+
* the peer regardless); runDshPlugin repeats it as
|
|
61
|
+
* PNPM_CONFIG_AUTO_INSTALL_PEERS, which 12 reads (#615).
|
|
50
62
|
*/
|
|
51
63
|
export const AUTO_INSTALL_PEERS_OFF = '--config.auto-install-peers=false';
|
|
52
64
|
/**
|
package/lib/restart.js
CHANGED
|
@@ -241,6 +241,10 @@ export function restartLaunch() {
|
|
|
241
241
|
* DSH sandbox tool runners) pops a visible node window. Wrapping the launch
|
|
242
242
|
* in `powershell -WindowStyle Hidden` gives the host a HIDDEN console that
|
|
243
243
|
* children inherit instead. POSIX keeps the plain detached spawn.
|
|
244
|
+
*
|
|
245
|
+
* The helper that runs this invocation spawns it with `windowsHide`, because
|
|
246
|
+
* the helper has no console of its own to hand down and Windows would
|
|
247
|
+
* otherwise create a visible one for PowerShell (#624).
|
|
244
248
|
*/
|
|
245
249
|
export function respawnInvocation(launch, platform = process.platform) {
|
|
246
250
|
if (platform !== 'win32') {
|
|
@@ -325,7 +329,17 @@ export function restartHelperSource(spawned, launch, logs, port) {
|
|
|
325
329
|
' try {',
|
|
326
330
|
' const out = fs.openSync(logOut, "a")',
|
|
327
331
|
' const err = fs.openSync(logErr, "a")',
|
|
328
|
-
|
|
332
|
+
// windowsHide (#624 by @davidekingsss): this helper is itself detached,
|
|
333
|
+
// so on Windows it has no console, and a console program spawned from a
|
|
334
|
+
// console-less parent is given a NEW, visible one — the "Windows
|
|
335
|
+
// PowerShell" window that owns the replacement and takes it down when
|
|
336
|
+
// closed. `-WindowStyle Hidden` cannot hide it: that flag governs the
|
|
337
|
+
// window PowerShell would create, not the console the spawn handed it.
|
|
338
|
+
// CREATE_NO_WINDOW keeps the console but never shows it, and the host's
|
|
339
|
+
// own console children inherit that hidden console rather than popping
|
|
340
|
+
// windows of their own (measured by the reporter with a console-less
|
|
341
|
+
// launcher: one PseudoConsoleWindow without the flag, none with it).
|
|
342
|
+
' child = spawn(file, args, { cwd, detached, stdio: ["ignore", out, err], env: process.env, shell: viaShell, windowsHide: true })',
|
|
329
343
|
// spawn reports a missing or unexecutable file ASYNCHRONOUSLY; the
|
|
330
344
|
// try/catch below only covers the synchronous throw, so without this
|
|
331
345
|
// listener that failure is exactly as silent as the bug being fixed.
|
package/lib/types/dsh-cli.d.ts
CHANGED
|
@@ -112,6 +112,23 @@ export declare function toolSearchDirs(platform?: string, env?: NodeJS.ProcessEn
|
|
|
112
112
|
* reports needs Windows, where the spawn is not detached.
|
|
113
113
|
*/
|
|
114
114
|
export declare function gitEnvForPnpm(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
115
|
+
/**
|
|
116
|
+
* Every `--config.<key>=<value>` override in the argv, repeated as the
|
|
117
|
+
* `PNPM_CONFIG_<KEY>` environment variable that pnpm 12 still reads.
|
|
118
|
+
*
|
|
119
|
+
* pnpm 12 ignores some `--config.<key>` overrides on the command line, in
|
|
120
|
+
* either spelling, without a word: `fetchTimeout` (#615; measured on
|
|
121
|
+
* 12.2.1, 12.3.0 and 12.4.1) and `auto-install-peers` (12.4.1 auto-installs
|
|
122
|
+
* the peer with the flag present and not with the variable), so the retries
|
|
123
|
+
* that carry them ran exactly like the first attempt. `PNPM_CONFIG_*` is
|
|
124
|
+
* honoured by 11.8, 11.21 and 12.4 alike, so the argument stays for the
|
|
125
|
+
* versions that read it and the variable carries the same value for the
|
|
126
|
+
* ones that do not. Scoped to the run that carries the flag; nothing is set
|
|
127
|
+
* otherwise, so a user's own values are untouched on every other run. Keys
|
|
128
|
+
* are accepted in either spelling, so respelling a constant (as #600 did
|
|
129
|
+
* for the release-age one) cannot silently drop the variable.
|
|
130
|
+
*/
|
|
131
|
+
export declare function pnpmConfigEnvForArgs(pluginArgs: readonly string[]): NodeJS.ProcessEnv;
|
|
115
132
|
/**
|
|
116
133
|
* Windows npm/corepack/pnpm are `.cmd` shims. Node's `spawn` without a shell
|
|
117
134
|
* cannot start them (ENOENT / EINVAL). Same pattern as dsh's `plugin` forwarder.
|
package/lib/types/install.d.ts
CHANGED
|
@@ -24,6 +24,14 @@ export declare const RELEASE_AGE_OVERRIDE = "--config.minimum-release-age=0";
|
|
|
24
24
|
* WHOLE repo even for a `#path:` subdirectory plugin) on slow networks; a
|
|
25
25
|
* plain retry fails again at the same limit, so the recovery re-runs with
|
|
26
26
|
* this override once. Scoped to a single command like RELEASE_AGE_OVERRIDE.
|
|
27
|
+
*
|
|
28
|
+
* pnpm 12 ignores this flag on the command line in either spelling (#615).
|
|
29
|
+
* On the CLI runner, runDshPlugin repeats every `--config.<key>` override
|
|
30
|
+
* as PNPM_CONFIG_<KEY>, which pnpm 11 and 12 both read, so the retried
|
|
31
|
+
* command really does get the longer limit there. The Desktop runtime hands
|
|
32
|
+
* its host argv only, so on a Desktop host the retry still depends on that
|
|
33
|
+
* host's pnpm reading the flag. The flag stays: pnpm 11 and earlier read
|
|
34
|
+
* it, and it costs nothing on the versions that do not.
|
|
27
35
|
*/
|
|
28
36
|
export declare const FETCH_TIMEOUT_OVERRIDE = "--config.fetchTimeout=600000";
|
|
29
37
|
/**
|
|
@@ -42,6 +50,10 @@ export declare const FETCH_TIMEOUT_OVERRIDE = "--config.fetchTimeout=600000";
|
|
|
42
50
|
* Verified against pnpm 10.29.3: `peerDependencyRules.ignoreMissing` does
|
|
43
51
|
* NOT prevent the fetch (it only silences the warning), so this flag is the
|
|
44
52
|
* only lever that actually works.
|
|
53
|
+
*
|
|
54
|
+
* pnpm 12 ignores this flag on the command line too (12.4.1 auto-installs
|
|
55
|
+
* the peer regardless); runDshPlugin repeats it as
|
|
56
|
+
* PNPM_CONFIG_AUTO_INSTALL_PEERS, which 12 reads (#615).
|
|
45
57
|
*/
|
|
46
58
|
export declare const AUTO_INSTALL_PEERS_OFF = "--config.auto-install-peers=false";
|
|
47
59
|
/**
|
package/lib/types/restart.d.ts
CHANGED
|
@@ -132,6 +132,10 @@ export declare function restartLaunch(): {
|
|
|
132
132
|
* DSH sandbox tool runners) pops a visible node window. Wrapping the launch
|
|
133
133
|
* in `powershell -WindowStyle Hidden` gives the host a HIDDEN console that
|
|
134
134
|
* children inherit instead. POSIX keeps the plain detached spawn.
|
|
135
|
+
*
|
|
136
|
+
* The helper that runs this invocation spawns it with `windowsHide`, because
|
|
137
|
+
* the helper has no console of its own to hand down and Windows would
|
|
138
|
+
* otherwise create a visible one for PowerShell (#624).
|
|
135
139
|
*/
|
|
136
140
|
export declare function respawnInvocation(launch: {
|
|
137
141
|
file: string;
|
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.
|
|
4
|
+
"version": "1.49.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -1375,6 +1375,11 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
1375
1375
|
const [buildsSkipped, setBuildsSkipped] = useState<{ plugin?: RegistryPlugin; updateName?: string; names: string[]; restore?: boolean } | null>(null)
|
|
1376
1376
|
const [updatingAll, setUpdatingAll] = useState(false)
|
|
1377
1377
|
const [updatedNames, setUpdatedNames] = useState<string[]>([])
|
|
1378
|
+
// #558: session-tracked updates the host actually parked behind a restart.
|
|
1379
|
+
// Kept separate from `updatedNames`, which doubles as the row-level
|
|
1380
|
+
// "updated" marker and therefore also records client-only updates that go
|
|
1381
|
+
// live without a restart.
|
|
1382
|
+
const [restartNames, setRestartNames] = useState<string[]>([])
|
|
1378
1383
|
const [hotUrls, setHotUrls] = useState<string[]>([])
|
|
1379
1384
|
const [hotNames, setHotNames] = useState<string[]>([])
|
|
1380
1385
|
const [progressLine, setProgressLine] = useState<string | null>(null)
|
|
@@ -1748,13 +1753,14 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
1748
1753
|
}
|
|
1749
1754
|
if (Array.isArray(saved.doneUrls) && saved.doneUrls.length > 0) setDoneUrls(saved.doneUrls)
|
|
1750
1755
|
if (Array.isArray(saved.updated) && saved.updated.length > 0) setUpdatedNames(saved.updated)
|
|
1756
|
+
if (Array.isArray(saved.restartNames) && saved.restartNames.length > 0) setRestartNames(saved.restartNames)
|
|
1751
1757
|
if (typeof saved.removed === 'number' && saved.removed > 0) setRemovedCount(saved.removed)
|
|
1752
1758
|
if (typeof saved.toggled === 'number' && saved.toggled > 0) setToggleRestart(saved.toggled)
|
|
1753
1759
|
}, [bootId])
|
|
1754
1760
|
|
|
1755
1761
|
useEffect(() => {
|
|
1756
1762
|
if (bootId === null) return
|
|
1757
|
-
if (doneUrls.length === 0 && updatedNames.length === 0 && removedCount === 0 && toggleRestart === 0) {
|
|
1763
|
+
if (doneUrls.length === 0 && updatedNames.length === 0 && restartNames.length === 0 && removedCount === 0 && toggleRestart === 0) {
|
|
1758
1764
|
// Nothing pending: drop any stale entry (e.g. a hot mount cleared the
|
|
1759
1765
|
// only doneUrl) so a same-boot remount cannot resurrect the banner (#73).
|
|
1760
1766
|
sessionStorage.removeItem('dshm-restart')
|
|
@@ -1764,10 +1770,11 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
1764
1770
|
boot: bootId,
|
|
1765
1771
|
doneUrls,
|
|
1766
1772
|
updated: updatedNames,
|
|
1773
|
+
restartNames,
|
|
1767
1774
|
removed: removedCount,
|
|
1768
1775
|
toggled: toggleRestart,
|
|
1769
1776
|
}))
|
|
1770
|
-
}, [bootId, doneUrls, updatedNames, removedCount, toggleRestart])
|
|
1777
|
+
}, [bootId, doneUrls, updatedNames, restartNames, removedCount, toggleRestart])
|
|
1771
1778
|
|
|
1772
1779
|
const fixEnv = useCallback(() => {
|
|
1773
1780
|
setEnvFixing(true)
|
|
@@ -2412,6 +2419,14 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
2412
2419
|
if (status === 200 && body.ok) {
|
|
2413
2420
|
setRecords(list => patchRecord(list, updateRecordId, { state: 'done' }))
|
|
2414
2421
|
setUpdatedNames(names => names.concat(name))
|
|
2422
|
+
// #558: the restart banner counts plugins the host parked behind a
|
|
2423
|
+
// restart, not every completed change. A client-only plugin comes
|
|
2424
|
+
// back 'inert'/'live' and goes live on refresh; when the host
|
|
2425
|
+
// reports no activation at all, count it to stay on the safe side.
|
|
2426
|
+
const activation = body.activation && typeof body.activation === 'object' ? body.activation[name] : undefined
|
|
2427
|
+
if (!activation || activation.state === 'restart') {
|
|
2428
|
+
setRestartNames(names => names.includes(name) ? names : names.concat(name))
|
|
2429
|
+
}
|
|
2415
2430
|
if (body.activation && typeof body.activation === 'object') {
|
|
2416
2431
|
setActivations(prev => ({ ...prev, ...body.activation }))
|
|
2417
2432
|
}
|
|
@@ -2972,7 +2987,11 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
2972
2987
|
// mid-run, which would strand the remaining items.
|
|
2973
2988
|
const selfName = installed['dshmarket'] !== undefined ? 'dshmarket' : 'dsh-market'
|
|
2974
2989
|
const updatableNames = Object.keys(installed).filter(
|
|
2975
|
-
name => name !== selfName
|
|
2990
|
+
name => name !== selfName
|
|
2991
|
+
&& !updatedNames.includes(name)
|
|
2992
|
+
&& !effectiveDisabledSet.has(name)
|
|
2993
|
+
&& updates[name]
|
|
2994
|
+
&& updates[name].updateAvailable,
|
|
2976
2995
|
)
|
|
2977
2996
|
// Replacing a local source with its catalog source is deliberately not a
|
|
2978
2997
|
// batch update: every such plugin has an existing, explicit confirmation
|
|
@@ -3233,7 +3252,10 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
3233
3252
|
if (Date.now() - last >= 24 * 60 * 60 * 1000) runWebdav('backup')
|
|
3234
3253
|
}, [autoBackup, runWebdav, webdavUrl, webdavUser])
|
|
3235
3254
|
|
|
3236
|
-
|
|
3255
|
+
// #558: count restart-pending session changes (restartNames), not every
|
|
3256
|
+
// completed `updatedNames` entry (which would double-count a batch update
|
|
3257
|
+
// that reports both a done URL and a name for the same plugin).
|
|
3258
|
+
const sessionPendingRestart = doneUrls.length + restartNames.length + removedCount + toggleRestart + (backupRestored ? 1 : 0)
|
|
3237
3259
|
/**
|
|
3238
3260
|
* Plugins the HOST reports as restart-pending, independent of what this
|
|
3239
3261
|
* browser session happens to remember. Installing and then reloading the
|
package/src/client/locales.ts
CHANGED
|
@@ -72,7 +72,7 @@ export const zh = {
|
|
|
72
72
|
installing: '安装中…',
|
|
73
73
|
installedBadge: '✓ 已安装',
|
|
74
74
|
alreadyInstalled: '✓ 已安装',
|
|
75
|
-
restartBanner: '
|
|
75
|
+
restartBanner: '项变更需重启 DeepSeek Harness 后生效',
|
|
76
76
|
uninstall: '卸载',
|
|
77
77
|
confirmRemove: '确认卸载?',
|
|
78
78
|
uninstalling: '卸载中…',
|
|
@@ -613,7 +613,7 @@ export const en: Record<MarketKey, string> = {
|
|
|
613
613
|
installing: 'Installing…',
|
|
614
614
|
installedBadge: '✓ Installed',
|
|
615
615
|
alreadyInstalled: '✓ Installed',
|
|
616
|
-
restartBanner: 'change(s)
|
|
616
|
+
restartBanner: 'change(s) pending restart — restart DeepSeek Harness to apply',
|
|
617
617
|
uninstall: 'Uninstall',
|
|
618
618
|
confirmRemove: 'Uninstall?',
|
|
619
619
|
uninstalling: 'Uninstalling…',
|
package/src/dsh-cli.ts
CHANGED
|
@@ -232,6 +232,33 @@ export function gitEnvForPnpm(env: NodeJS.ProcessEnv = process.env): NodeJS.Proc
|
|
|
232
232
|
return { GIT_TERMINAL_PROMPT: '0' }
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Every `--config.<key>=<value>` override in the argv, repeated as the
|
|
237
|
+
* `PNPM_CONFIG_<KEY>` environment variable that pnpm 12 still reads.
|
|
238
|
+
*
|
|
239
|
+
* pnpm 12 ignores some `--config.<key>` overrides on the command line, in
|
|
240
|
+
* either spelling, without a word: `fetchTimeout` (#615; measured on
|
|
241
|
+
* 12.2.1, 12.3.0 and 12.4.1) and `auto-install-peers` (12.4.1 auto-installs
|
|
242
|
+
* the peer with the flag present and not with the variable), so the retries
|
|
243
|
+
* that carry them ran exactly like the first attempt. `PNPM_CONFIG_*` is
|
|
244
|
+
* honoured by 11.8, 11.21 and 12.4 alike, so the argument stays for the
|
|
245
|
+
* versions that read it and the variable carries the same value for the
|
|
246
|
+
* ones that do not. Scoped to the run that carries the flag; nothing is set
|
|
247
|
+
* otherwise, so a user's own values are untouched on every other run. Keys
|
|
248
|
+
* are accepted in either spelling, so respelling a constant (as #600 did
|
|
249
|
+
* for the release-age one) cannot silently drop the variable.
|
|
250
|
+
*/
|
|
251
|
+
export function pnpmConfigEnvForArgs(pluginArgs: readonly string[]): NodeJS.ProcessEnv {
|
|
252
|
+
const env: NodeJS.ProcessEnv = {}
|
|
253
|
+
for (const arg of pluginArgs) {
|
|
254
|
+
const match = /^--config\.([A-Za-z][A-Za-z0-9-]*)=(\S+)$/.exec(arg)
|
|
255
|
+
if (match === null) continue
|
|
256
|
+
const key = match[1]!.replace(/([a-z0-9])([A-Z])/g, '$1_$2').replace(/-/g, '_').toUpperCase()
|
|
257
|
+
env[`PNPM_CONFIG_${key}`] = match[2]!
|
|
258
|
+
}
|
|
259
|
+
return env
|
|
260
|
+
}
|
|
261
|
+
|
|
235
262
|
function spawnEnv(): NodeJS.ProcessEnv {
|
|
236
263
|
// pnpm v10+ blocks forever on a silent interactive prompt without a TTY;
|
|
237
264
|
// CI mode forces it to act or fail instead of asking.
|
|
@@ -913,7 +940,7 @@ export function runDshPlugin(profile: string, pluginArgs: string[]): Promise<Ins
|
|
|
913
940
|
// pnpm v10 blocks forever on a silent interactive prompt without a TTY
|
|
914
941
|
// (observed on re-add over a pinned git spec); CI mode forces it to act
|
|
915
942
|
// or fail instead of asking.
|
|
916
|
-
env: spawnEnv(),
|
|
943
|
+
env: { ...spawnEnv(), ...pnpmConfigEnvForArgs(pluginArgs) },
|
|
917
944
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
918
945
|
viaShell,
|
|
919
946
|
// Own process group on POSIX so cancel/timeout can kill the whole
|
package/src/install.ts
CHANGED
|
@@ -33,6 +33,14 @@ export const RELEASE_AGE_OVERRIDE = '--config.minimum-release-age=0'
|
|
|
33
33
|
* WHOLE repo even for a `#path:` subdirectory plugin) on slow networks; a
|
|
34
34
|
* plain retry fails again at the same limit, so the recovery re-runs with
|
|
35
35
|
* this override once. Scoped to a single command like RELEASE_AGE_OVERRIDE.
|
|
36
|
+
*
|
|
37
|
+
* pnpm 12 ignores this flag on the command line in either spelling (#615).
|
|
38
|
+
* On the CLI runner, runDshPlugin repeats every `--config.<key>` override
|
|
39
|
+
* as PNPM_CONFIG_<KEY>, which pnpm 11 and 12 both read, so the retried
|
|
40
|
+
* command really does get the longer limit there. The Desktop runtime hands
|
|
41
|
+
* its host argv only, so on a Desktop host the retry still depends on that
|
|
42
|
+
* host's pnpm reading the flag. The flag stays: pnpm 11 and earlier read
|
|
43
|
+
* it, and it costs nothing on the versions that do not.
|
|
36
44
|
*/
|
|
37
45
|
export const FETCH_TIMEOUT_OVERRIDE = '--config.fetchTimeout=600000'
|
|
38
46
|
|
|
@@ -52,6 +60,10 @@ export const FETCH_TIMEOUT_OVERRIDE = '--config.fetchTimeout=600000'
|
|
|
52
60
|
* Verified against pnpm 10.29.3: `peerDependencyRules.ignoreMissing` does
|
|
53
61
|
* NOT prevent the fetch (it only silences the warning), so this flag is the
|
|
54
62
|
* only lever that actually works.
|
|
63
|
+
*
|
|
64
|
+
* pnpm 12 ignores this flag on the command line too (12.4.1 auto-installs
|
|
65
|
+
* the peer regardless); runDshPlugin repeats it as
|
|
66
|
+
* PNPM_CONFIG_AUTO_INSTALL_PEERS, which 12 reads (#615).
|
|
55
67
|
*/
|
|
56
68
|
export const AUTO_INSTALL_PEERS_OFF = '--config.auto-install-peers=false'
|
|
57
69
|
|
package/src/restart.ts
CHANGED
|
@@ -246,6 +246,10 @@ export function restartLaunch(): { file: string; args: string[]; cwd: string; vi
|
|
|
246
246
|
* DSH sandbox tool runners) pops a visible node window. Wrapping the launch
|
|
247
247
|
* in `powershell -WindowStyle Hidden` gives the host a HIDDEN console that
|
|
248
248
|
* children inherit instead. POSIX keeps the plain detached spawn.
|
|
249
|
+
*
|
|
250
|
+
* The helper that runs this invocation spawns it with `windowsHide`, because
|
|
251
|
+
* the helper has no console of its own to hand down and Windows would
|
|
252
|
+
* otherwise create a visible one for PowerShell (#624).
|
|
249
253
|
*/
|
|
250
254
|
export function respawnInvocation(
|
|
251
255
|
launch: { file: string; args: string[]; viaShell: boolean },
|
|
@@ -347,7 +351,17 @@ export function restartHelperSource(
|
|
|
347
351
|
' try {',
|
|
348
352
|
' const out = fs.openSync(logOut, "a")',
|
|
349
353
|
' const err = fs.openSync(logErr, "a")',
|
|
350
|
-
|
|
354
|
+
// windowsHide (#624 by @davidekingsss): this helper is itself detached,
|
|
355
|
+
// so on Windows it has no console, and a console program spawned from a
|
|
356
|
+
// console-less parent is given a NEW, visible one — the "Windows
|
|
357
|
+
// PowerShell" window that owns the replacement and takes it down when
|
|
358
|
+
// closed. `-WindowStyle Hidden` cannot hide it: that flag governs the
|
|
359
|
+
// window PowerShell would create, not the console the spawn handed it.
|
|
360
|
+
// CREATE_NO_WINDOW keeps the console but never shows it, and the host's
|
|
361
|
+
// own console children inherit that hidden console rather than popping
|
|
362
|
+
// windows of their own (measured by the reporter with a console-less
|
|
363
|
+
// launcher: one PseudoConsoleWindow without the flag, none with it).
|
|
364
|
+
' child = spawn(file, args, { cwd, detached, stdio: ["ignore", out, err], env: process.env, shell: viaShell, windowsHide: true })',
|
|
351
365
|
// spawn reports a missing or unexecutable file ASYNCHRONOUSLY; the
|
|
352
366
|
// try/catch below only covers the synchronous throw, so without this
|
|
353
367
|
// listener that failure is exactly as silent as the bug being fixed.
|