dshmarket 1.66.28 → 1.66.30
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 +21 -21
- package/lib/install.js +15 -0
- package/lib/pnpm-compat.js +20 -1
- package/package.json +1 -1
- package/src/client/HostRecoveryBanner.tsx +2 -1
- package/src/install.ts +14 -0
- package/src/pnpm-compat.ts +20 -1
package/client/client.js
CHANGED
|
@@ -193,7 +193,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
193
193
|
const IconRefreshOutline14 = pickIcon("IconRefreshOutlineRegular", "IconRefreshOutline14");
|
|
194
194
|
const IconSearchOutline16 = pickIcon("IconSearchOutlineRegular", "IconSearchOutline16");
|
|
195
195
|
const IconSparkle16 = pickIcon("IconSparkleRegular", "IconSparkle16");
|
|
196
|
-
const IconWarningOutline16
|
|
196
|
+
const IconWarningOutline16 = pickIcon("IconWarningOutlineRegular", "IconWarningOutline16");
|
|
197
197
|
//#endregion
|
|
198
198
|
//#region src/client/locales.ts
|
|
199
199
|
/** zh/en dictionaries for the Market settings section and install toast. */
|
|
@@ -4192,7 +4192,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
4192
4192
|
size: 13,
|
|
4193
4193
|
className: Market_module_css_default.reassureOk
|
|
4194
4194
|
});
|
|
4195
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
4195
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
|
|
4196
4196
|
size: 14,
|
|
4197
4197
|
className: Market_module_css_default.conflictIcon
|
|
4198
4198
|
});
|
|
@@ -4574,14 +4574,14 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
4574
4574
|
children: [
|
|
4575
4575
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4576
4576
|
className: Market_module_css_default.recoverySummary,
|
|
4577
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
4577
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
|
|
4578
4578
|
size: 14,
|
|
4579
4579
|
className: Market_module_css_default.bannerIcon
|
|
4580
4580
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: view.failure.summary || t("recoveryNoSummary") })]
|
|
4581
4581
|
}),
|
|
4582
4582
|
view.lastErrors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4583
4583
|
className: Market_module_css_default.recoverySummary,
|
|
4584
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
4584
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
|
|
4585
4585
|
size: 14,
|
|
4586
4586
|
className: Market_module_css_default.bannerIcon
|
|
4587
4587
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("recoveryWriteFailed"), view.lastErrors.join("; ")] })]
|
|
@@ -4759,7 +4759,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
4759
4759
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4760
4760
|
className: Market_module_css_default.banner,
|
|
4761
4761
|
children: [
|
|
4762
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
4762
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
|
|
4763
4763
|
size: 14,
|
|
4764
4764
|
className: Market_module_css_default.bannerIcon
|
|
4765
4765
|
}),
|
|
@@ -6679,7 +6679,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
6679
6679
|
const remaining = findings.length - preview.length;
|
|
6680
6680
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6681
6681
|
className: Market_module_css_default.banner,
|
|
6682
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
6682
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
|
|
6683
6683
|
size: 14,
|
|
6684
6684
|
className: Market_module_css_default.bannerIcon
|
|
6685
6685
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -11477,7 +11477,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11477
11477
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
11478
11478
|
redLines.filter(redLineIsUrgent).map((line) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
11479
11479
|
className: Market_module_css_default.warnLine,
|
|
11480
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
11480
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
|
|
11481
11481
|
size: 14,
|
|
11482
11482
|
className: Market_module_css_default.bannerIcon
|
|
11483
11483
|
}), " " + redLineLabel(line)]
|
|
@@ -11676,12 +11676,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11676
11676
|
type: "button",
|
|
11677
11677
|
className: Market_module_css_default.cardBlockedMark,
|
|
11678
11678
|
onClick: openOperations,
|
|
11679
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
11679
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t("queuedBadge")]
|
|
11680
11680
|
}) : blocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
11681
11681
|
type: "button",
|
|
11682
11682
|
className: Market_module_css_default.cardBlockedMark,
|
|
11683
11683
|
onClick: openOperations,
|
|
11684
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
11684
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t("opBlockedCard")]
|
|
11685
11685
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
11686
11686
|
variant: "primary",
|
|
11687
11687
|
size: "sm",
|
|
@@ -11898,12 +11898,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11898
11898
|
type: "button",
|
|
11899
11899
|
className: Market_module_css_default.cardBlockedMark,
|
|
11900
11900
|
onClick: openOperations,
|
|
11901
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
11901
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t("queuedBadge")]
|
|
11902
11902
|
}) : blocked ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
11903
11903
|
type: "button",
|
|
11904
11904
|
className: Market_module_css_default.cardBlockedMark,
|
|
11905
11905
|
onClick: openOperations,
|
|
11906
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
11906
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 13 }), t("opBlockedCard")]
|
|
11907
11907
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
11908
11908
|
variant: "primary",
|
|
11909
11909
|
size: "sm",
|
|
@@ -12353,7 +12353,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
12353
12353
|
}),
|
|
12354
12354
|
restoreErrors.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
12355
12355
|
className: Market_module_css_default.banner,
|
|
12356
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
12356
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
|
|
12357
12357
|
size: 14,
|
|
12358
12358
|
className: Market_module_css_default.bannerIcon
|
|
12359
12359
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -12465,7 +12465,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
12465
12465
|
}),
|
|
12466
12466
|
activationWarnings.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
12467
12467
|
className: Market_module_css_default.banner,
|
|
12468
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
12468
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
|
|
12469
12469
|
size: 14,
|
|
12470
12470
|
className: Market_module_css_default.bannerIcon
|
|
12471
12471
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -12487,7 +12487,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
12487
12487
|
}),
|
|
12488
12488
|
tab === "installed" && configNamespaceIssues.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
12489
12489
|
className: Market_module_css_default.banner,
|
|
12490
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
12490
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
|
|
12491
12491
|
size: 14,
|
|
12492
12492
|
className: Market_module_css_default.bannerIcon
|
|
12493
12493
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -12540,7 +12540,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
12540
12540
|
buildsSkipped !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
12541
12541
|
className: Market_module_css_default.banner,
|
|
12542
12542
|
children: [
|
|
12543
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
12543
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, {
|
|
12544
12544
|
size: 14,
|
|
12545
12545
|
className: Market_module_css_default.bannerIcon
|
|
12546
12546
|
}),
|
|
@@ -14365,7 +14365,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
14365
14365
|
]
|
|
14366
14366
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
14367
14367
|
className: Market_module_css_default.migrationWarning,
|
|
14368
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
14368
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }), t("migrateWarning")]
|
|
14369
14369
|
})]
|
|
14370
14370
|
}),
|
|
14371
14371
|
agentsConfirm !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
@@ -14706,17 +14706,17 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
14706
14706
|
}),
|
|
14707
14707
|
exportState === "fail" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
14708
14708
|
text: t("exportLogFail"),
|
|
14709
|
-
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
14709
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }),
|
|
14710
14710
|
onDone: exportToastDone
|
|
14711
14711
|
}),
|
|
14712
14712
|
favoriteError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
14713
14713
|
text: localizeBilingual(favoriteError, lang),
|
|
14714
|
-
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
14714
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }),
|
|
14715
14715
|
onDone: favoriteErrorDone
|
|
14716
14716
|
}),
|
|
14717
14717
|
blockError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
14718
14718
|
text: localizeBilingual(blockError, lang),
|
|
14719
|
-
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
14719
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }),
|
|
14720
14720
|
onDone: blockErrorDone
|
|
14721
14721
|
}),
|
|
14722
14722
|
blockNotice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
@@ -14725,7 +14725,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
14725
14725
|
}),
|
|
14726
14726
|
updateExemptError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
14727
14727
|
text: localizeBilingual(updateExemptError, lang),
|
|
14728
|
-
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
14728
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }),
|
|
14729
14729
|
onDone: updateExemptErrorDone
|
|
14730
14730
|
}),
|
|
14731
14731
|
updateExemptNotice !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
@@ -14734,7 +14734,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
14734
14734
|
}),
|
|
14735
14735
|
toggled !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
14736
14736
|
text: toggled.name + " " + t(toggled.enabled ? "toastToggledOn" : "toastToggledOff"),
|
|
14737
|
-
icon: toggled.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCheckOutline16, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16
|
|
14737
|
+
icon: toggled.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconCheckOutline16, { size: 14 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconWarningOutline16, { size: 14 }),
|
|
14738
14738
|
onDone: toggledDone
|
|
14739
14739
|
})
|
|
14740
14740
|
]
|
package/lib/install.js
CHANGED
|
@@ -339,6 +339,21 @@ export async function withHoistRecovery(run, profile, pluginArgs, profileDirecto
|
|
|
339
339
|
: `这台宿主(官方桌面端)自己执行安装,不接受市场附加的 pnpm 参数(${unavailableOption}),所以这一步没有自动重试。 / This host (the official desktop app) runs the install itself and takes no pnpm options from the market (${unavailableOption}), so that automatic retry could not be attempted.`;
|
|
340
340
|
result = { ...result, stderr: `${result.stderr}\n\n${note}` };
|
|
341
341
|
}
|
|
342
|
+
// pnpm can append a second rule even on success (#883, #878), including
|
|
343
|
+
// after a recovery retry or an official Desktop manager install. Repair
|
|
344
|
+
// the final file now, before another plugin operation reads it; a successful
|
|
345
|
+
// command must never be retried just because its exclusions needed merging.
|
|
346
|
+
if (ok(result) && (verb === 'add' || verb === 'remove' || verb === 'install' || verb === 'update')) {
|
|
347
|
+
try {
|
|
348
|
+
const merged = mergeDuplicateReleaseAgeExcludes(profile, profileDirectory);
|
|
349
|
+
if (merged.length > 0) {
|
|
350
|
+
logEvent('warn', 'install', `merged duplicate minimumReleaseAgeExclude rules for ${merged.join(', ')} after success (#883, #878)`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
logEvent('warn', 'install', `could not merge minimumReleaseAgeExclude after success (#883, #878): ${String(error)}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
342
357
|
return result;
|
|
343
358
|
}
|
|
344
359
|
/**
|
package/lib/pnpm-compat.js
CHANGED
|
@@ -362,8 +362,27 @@ export function classifyPnpmFailure(output, exitCode) {
|
|
|
362
362
|
// (MINIMUM_RELEASE_AGE_VIOLATION), and a later add can fail re-resolving
|
|
363
363
|
// the young dep (NO_MATURE_MATCHING_VERSION). Recovery is a one-shot
|
|
364
364
|
// --config.minimum-release-age=0 retry, automated in withHoistRecovery.
|
|
365
|
+
//
|
|
366
|
+
// That gate has a THIRD face, and it is the one that fails AFTER the
|
|
367
|
+
// lockfile check has said yes. pnpm skips that check when the verification
|
|
368
|
+
// cache holds a run whose recorded window is at least as wide as the
|
|
369
|
+
// current one (`canTrustPastCheck`: past >= current, pnpm's own
|
|
370
|
+
// lockfile-verified cache), and the resolver then applies the same policy
|
|
371
|
+
// from scratch. A re-resolved dependency still inside the window comes back
|
|
372
|
+
// as a `policyViolation`, which the CLI only knows how to consume through a
|
|
373
|
+
// handler it never wires on this call path — so it throws
|
|
374
|
+
// RESOLUTION_POLICY_VIOLATIONS_UNHANDLED instead of reporting anything the
|
|
375
|
+
// user can act on. Measured on the bundled pnpm 11.7.0 (DSH Desktop), with
|
|
376
|
+
// a cached pass in place: verification prints "passes supply-chain
|
|
377
|
+
// policies", and the next phase dies with "2 resolution-policy violations
|
|
378
|
+
// were produced but no handleResolutionPolicyViolations callback was wired".
|
|
379
|
+
// Same cause, same cure as the two shapes above — pnpm's hint names
|
|
380
|
+
// `setupPolicyHandlers`, which an integration calling the CLI cannot wire,
|
|
381
|
+
// so the only answer is the one-shot override. Classifying it here is what
|
|
382
|
+
// connects it to that retry; without it the raw internal hint was surfaced.
|
|
365
383
|
if (output.includes('ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION')
|
|
366
|
-
|| output.includes('ERR_PNPM_NO_MATURE_MATCHING_VERSION')
|
|
384
|
+
|| output.includes('ERR_PNPM_NO_MATURE_MATCHING_VERSION')
|
|
385
|
+
|| output.includes('ERR_PNPM_RESOLUTION_POLICY_VIOLATIONS_UNHANDLED')) {
|
|
367
386
|
return {
|
|
368
387
|
code: 'release-age-violation',
|
|
369
388
|
recoverable: false,
|
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.66.
|
|
4
|
+
"version": "1.66.30",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
* see — the two patches side by side.
|
|
16
16
|
*/
|
|
17
17
|
import { useCallback, useEffect, useState, type ReactElement } from 'react'
|
|
18
|
-
import { Button
|
|
18
|
+
import { Button } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
19
|
+
import { IconWarningOutline16 } from './icons.ts'
|
|
19
20
|
import css from './Market.module.css'
|
|
20
21
|
import { api } from './market-data.ts'
|
|
21
22
|
import type { Translate } from './market-data.ts'
|
package/src/install.ts
CHANGED
|
@@ -345,6 +345,20 @@ export async function withHoistRecovery(
|
|
|
345
345
|
: `这台宿主(官方桌面端)自己执行安装,不接受市场附加的 pnpm 参数(${unavailableOption}),所以这一步没有自动重试。 / This host (the official desktop app) runs the install itself and takes no pnpm options from the market (${unavailableOption}), so that automatic retry could not be attempted.`
|
|
346
346
|
result = { ...result, stderr: `${result.stderr}\n\n${note}` }
|
|
347
347
|
}
|
|
348
|
+
// pnpm can append a second rule even on success (#883, #878), including
|
|
349
|
+
// after a recovery retry or an official Desktop manager install. Repair
|
|
350
|
+
// the final file now, before another plugin operation reads it; a successful
|
|
351
|
+
// command must never be retried just because its exclusions needed merging.
|
|
352
|
+
if (ok(result) && (verb === 'add' || verb === 'remove' || verb === 'install' || verb === 'update')) {
|
|
353
|
+
try {
|
|
354
|
+
const merged = mergeDuplicateReleaseAgeExcludes(profile, profileDirectory)
|
|
355
|
+
if (merged.length > 0) {
|
|
356
|
+
logEvent('warn', 'install', `merged duplicate minimumReleaseAgeExclude rules for ${merged.join(', ')} after success (#883, #878)`)
|
|
357
|
+
}
|
|
358
|
+
} catch (error) {
|
|
359
|
+
logEvent('warn', 'install', `could not merge minimumReleaseAgeExclude after success (#883, #878): ${String(error)}`)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
348
362
|
return result
|
|
349
363
|
}
|
|
350
364
|
|
package/src/pnpm-compat.ts
CHANGED
|
@@ -399,8 +399,27 @@ export function classifyPnpmFailure(output: string, exitCode?: number | null): P
|
|
|
399
399
|
// (MINIMUM_RELEASE_AGE_VIOLATION), and a later add can fail re-resolving
|
|
400
400
|
// the young dep (NO_MATURE_MATCHING_VERSION). Recovery is a one-shot
|
|
401
401
|
// --config.minimum-release-age=0 retry, automated in withHoistRecovery.
|
|
402
|
+
//
|
|
403
|
+
// That gate has a THIRD face, and it is the one that fails AFTER the
|
|
404
|
+
// lockfile check has said yes. pnpm skips that check when the verification
|
|
405
|
+
// cache holds a run whose recorded window is at least as wide as the
|
|
406
|
+
// current one (`canTrustPastCheck`: past >= current, pnpm's own
|
|
407
|
+
// lockfile-verified cache), and the resolver then applies the same policy
|
|
408
|
+
// from scratch. A re-resolved dependency still inside the window comes back
|
|
409
|
+
// as a `policyViolation`, which the CLI only knows how to consume through a
|
|
410
|
+
// handler it never wires on this call path — so it throws
|
|
411
|
+
// RESOLUTION_POLICY_VIOLATIONS_UNHANDLED instead of reporting anything the
|
|
412
|
+
// user can act on. Measured on the bundled pnpm 11.7.0 (DSH Desktop), with
|
|
413
|
+
// a cached pass in place: verification prints "passes supply-chain
|
|
414
|
+
// policies", and the next phase dies with "2 resolution-policy violations
|
|
415
|
+
// were produced but no handleResolutionPolicyViolations callback was wired".
|
|
416
|
+
// Same cause, same cure as the two shapes above — pnpm's hint names
|
|
417
|
+
// `setupPolicyHandlers`, which an integration calling the CLI cannot wire,
|
|
418
|
+
// so the only answer is the one-shot override. Classifying it here is what
|
|
419
|
+
// connects it to that retry; without it the raw internal hint was surfaced.
|
|
402
420
|
if (output.includes('ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION')
|
|
403
|
-
|| output.includes('ERR_PNPM_NO_MATURE_MATCHING_VERSION')
|
|
421
|
+
|| output.includes('ERR_PNPM_NO_MATURE_MATCHING_VERSION')
|
|
422
|
+
|| output.includes('ERR_PNPM_RESOLUTION_POLICY_VIOLATIONS_UNHANDLED')) {
|
|
404
423
|
return {
|
|
405
424
|
code: 'release-age-violation',
|
|
406
425
|
recoverable: false,
|