dshmarket 1.66.22 → 1.66.23

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 CHANGED
@@ -557,6 +557,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
557
557
  marketUpdate: "更新插件市场",
558
558
  updateAll: "全部更新",
559
559
  heldReleaseNotice: "已安装,但不是最新版:这个 profile 的 minimumReleaseAge 挡住了 {0},安装的是 {1}。",
560
+ installedDependencyNote: "另有 {0} 个包是插件带进来的依赖,不是插件,未列在上方。",
560
561
  heldReleaseAction: "仍安装 {0}",
561
562
  ignoreUpdateNotice: "重启前不再提醒",
562
563
  ignoreAllUpdateNotices: "重启前都不再提醒",
@@ -1273,6 +1274,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1273
1274
  marketUpdate: "Update the plugin market",
1274
1275
  updateAll: "Update all",
1275
1276
  heldReleaseNotice: "Installed, but not the newest: this profile's minimumReleaseAge held {0} back, so {1} was installed.",
1277
+ installedDependencyNote: "{0} more packages came in as dependencies of plugins — they are not plugins, so they are not listed above.",
1276
1278
  heldReleaseAction: "Install {0} anyway",
1277
1279
  ignoreUpdateNotice: "Don't remind until restart",
1278
1280
  ignoreAllUpdateNotices: "Don't remind any until restart",
@@ -10641,7 +10643,19 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
10641
10643
  const reminderUpdatableNames = updatableNames.filter((name) => !quietUpdateSet.has(name) && !installedBlocked(name));
10642
10644
  const reminderBatchUpdatableNames = batchUpdatableNames.filter((name) => !quietUpdateSet.has(name) && !installedBlocked(name));
10643
10645
  const reminderUpdateNames = [...updates[selfName]?.updateAvailable === true && !updatedNames.includes(selfName) ? [selfName] : [], ...updatableNames].filter((name) => !quietUpdateSet.has(name) && !installedBlocked(name));
10644
- const installedOtherCount = Object.keys(installed).filter((name) => name !== selfName).length;
10646
+ /**
10647
+ * Installed packages that are not plugins at all: a plain library some
10648
+ * other plugin pulled in — zustand, bufferutil, a peer of ws (#634's
10649
+ * `dependencyOf`, #857). They were rendered as plugin cards with a
10650
+ * working uninstall, which is a real remove path for something a plugin
10651
+ * needs. Out of the list; the count stays visible in one quiet line, so the
10652
+ * list is not silently shorter than package.json.
10653
+ */
10654
+ const dependencyOnlyNames = (0, react.useMemo)(() => new Set(Object.keys(installed).filter((name) => {
10655
+ const act = activations[name];
10656
+ return act?.state === "inert" && act.dependencyOf !== void 0;
10657
+ })), [installed, activations]);
10658
+ const installedOtherCount = Object.keys(installed).filter((name) => name !== selfName && !dependencyOnlyNames.has(name)).length;
10645
10659
  const ignoreUpdateNotices = (0, react.useCallback)((names) => {
10646
10660
  if (bootId === null || names.length === 0) return;
10647
10661
  setIgnoredUpdateNames((current) => {
@@ -10936,7 +10950,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
10936
10950
  const isInstalledListActive = tab === "installed" && installedView === "list";
10937
10951
  const updatesLoaded = Object.keys(updates).length > 0;
10938
10952
  const orderedInstalledEntries = (0, react.useMemo)(() => {
10939
- return Object.entries(displayedInstalled).filter(([name]) => name !== selfName).sort(([nameA, specA], [nameB, specB]) => {
10953
+ return Object.entries(displayedInstalled).filter(([name]) => name !== selfName).filter(([name]) => !dependencyOnlyNames.has(name)).sort(([nameA, specA], [nameB, specB]) => {
10940
10954
  const aOn = effectiveDisabledSet.has(nameA) ? 0 : 1;
10941
10955
  const bOn = effectiveDisabledSet.has(nameB) ? 0 : 1;
10942
10956
  if (aOn !== bOn) return bOn - aOn;
@@ -10948,7 +10962,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
10948
10962
  displayedInstalled,
10949
10963
  selfName,
10950
10964
  updatesLoaded,
10951
- effectiveDisabledSet
10965
+ effectiveDisabledSet,
10966
+ dependencyOnlyNames
10952
10967
  ]);
10953
10968
  const missingRestoreCount = Object.keys(pendingDependencies).filter((name) => !installedFiles.includes(name)).length;
10954
10969
  const hasUpdates = reminderUpdatableNames.length > 0;
@@ -11770,7 +11785,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
11770
11785
  */
11771
11786
  const pendingRefreshNames = (0, react.useMemo)(() => [.../* @__PURE__ */ new Set([...hotNames, ...refreshNames])], [hotNames, refreshNames]);
11772
11787
  /** Installed plugins the market itself cannot group (#60). */
11773
- const groupableNames = Object.keys(installed).filter((name) => name !== "dsh-market" && name !== "dshmarket");
11788
+ const groupableNames = Object.keys(installed).filter((name) => name !== "dsh-market" && name !== "dshmarket" && !dependencyOnlyNames.has(name));
11774
11789
  /** Names already inside some group; everything else shows under "ungrouped". */
11775
11790
  const groupedNames = (0, react.useMemo)(() => new Set(Object.values(groups).flat()), [groups]);
11776
11791
  const ungroupedNames = groupableNames.filter((name) => !groupedNames.has(name));
@@ -13641,6 +13656,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
13641
13656
  })]
13642
13657
  }, name);
13643
13658
  }
13659
+ }),
13660
+ dependencyOnlyNames.size > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
13661
+ className: Market_module_css_default.groupOrgHint,
13662
+ children: t("installedDependencyNote").replace("{0}", String(dependencyOnlyNames.size))
13644
13663
  })
13645
13664
  ] })
13646
13665
  }),
package/lib/install.js CHANGED
@@ -328,10 +328,16 @@ export async function withHoistRecovery(run, profile, pluginArgs, profileDirecto
328
328
  // no options from us — but the classified message above says the market
329
329
  // retried. Say that it did not, or the user reads a retry that never
330
330
  // happened and looks for the wrong cause.
331
- result = {
332
- ...result,
333
- stderr: `${result.stderr}\n\n这台宿主(官方桌面端)自己执行安装,不接受市场附加的 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.`,
334
- };
331
+ //
332
+ // The wait on a fresh release is the one of these where "no retry" leaves
333
+ // the user with nothing to do at all: the plugin IS published, pnpm just
334
+ // will not take it yet, and the door the market would open on a plain
335
+ // `dsh web` host is shut here — so say when the same button starts
336
+ // working, and what to use meanwhile (#852).
337
+ const note = unavailableOption === RELEASE_AGE_OVERRIDE
338
+ ? `这个插件的新版本比本 profile 的 minimumReleaseAge(新版本等待期)还新,pnpm 因此拒绝了本次改动(改别的插件也一样)。官方桌面端由应用自己执行安装,不接受市场的临时放行参数(${unavailableOption}),所以这里没有自动重试。**等这个版本过了等待期再更新一次即可**;想现在就更新,可以在普通的 dsh web 里装。 / The plugin's new version is younger than this profile's minimumReleaseAge, so pnpm refused this change (a change to another plugin too). The official desktop app runs the install itself and takes no one-shot bypass from the market (${unavailableOption}), so no automatic retry happens here. **Update again once the version is past the wait**; to update right now, install from plain dsh web.`
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
+ result = { ...result, stderr: `${result.stderr}\n\n${note}` };
335
341
  }
336
342
  return result;
337
343
  }
@@ -426,7 +432,27 @@ export function failureDetail(result, limit = 300) {
426
432
  const code = result.pnpmErrorCode === undefined ? '' : `${result.pnpmErrorCode}: `;
427
433
  return `${code}${result.pnpmError}`.slice(0, limit);
428
434
  }
429
- return (result.stderr || result.stdout).slice(-limit);
435
+ const text = (result.stderr || result.stdout).trim();
436
+ if (text.length <= limit)
437
+ return text;
438
+ const tail = text.slice(-limit);
439
+ // Cut at a sentence end instead of mid-word, and say that something was
440
+ // dropped (#852). The market appends its own explanation to pnpm's output,
441
+ // and this tail is catalogued into user-facing sentences — including inside
442
+ // a parenthesis. A blunt cut landed inside the previous sentence and welded
443
+ // the fragment to the next one: the reporter's log read
444
+ // `(is, export the log and report it这台宿主…`, which is not a sentence in
445
+ // either language and says nothing about what to do.
446
+ const sentenceEnd = /[.!?。!?]["')\])]?[\s\n]+/gu;
447
+ const match = sentenceEnd.exec(tail);
448
+ if (match !== null) {
449
+ const rest = tail.slice(match.index + match[0].length).trim();
450
+ // A boundary so near the end would leave a word or two; the raw tail is
451
+ // more use than the stub.
452
+ if (rest.length >= 40)
453
+ return `… ${rest}`;
454
+ }
455
+ return `…${tail.trim()}`;
430
456
  }
431
457
  /**
432
458
  * Some registry entries point at collection repos whose actual plugin lives
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.22",
4
+ "version": "1.66.23",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -4462,7 +4462,23 @@ export function MarketSection(props: MarketSectionProps) {
4462
4462
  // The market manages itself from its own settings card (Settings → Plugins
4463
4463
  // → Plugin configuration), not as a row here — listing it in both places
4464
4464
  // read as two different controls for the same thing.
4465
- const installedOtherCount = Object.keys(installed).filter(name => name !== selfName).length
4465
+ /**
4466
+ * Installed packages that are not plugins at all: a plain library some
4467
+ * other plugin pulled in — zustand, bufferutil, a peer of ws (#634's
4468
+ * `dependencyOf`, #857). They were rendered as plugin cards with a
4469
+ * working uninstall, which is a real remove path for something a plugin
4470
+ * needs. Out of the list; the count stays visible in one quiet line, so the
4471
+ * list is not silently shorter than package.json.
4472
+ */
4473
+ const dependencyOnlyNames = useMemo(
4474
+ () => new Set(Object.keys(installed).filter(name => {
4475
+ const act = activations[name]
4476
+ return act?.state === 'inert' && act.dependencyOf !== undefined
4477
+ })),
4478
+ [installed, activations],
4479
+ )
4480
+ const installedOtherCount = Object.keys(installed)
4481
+ .filter(name => name !== selfName && !dependencyOnlyNames.has(name)).length
4466
4482
 
4467
4483
  const ignoreUpdateNotices = useCallback((names: string[]) => {
4468
4484
  if (bootId === null || names.length === 0) return
@@ -4759,6 +4775,7 @@ export function MarketSection(props: MarketSectionProps) {
4759
4775
  const orderedInstalledEntries = useMemo(() => {
4760
4776
  return Object.entries(displayedInstalled)
4761
4777
  .filter(([name]) => name !== selfName)
4778
+ .filter(([name]) => !dependencyOnlyNames.has(name))
4762
4779
  .sort(([nameA, specA], [nameB, specB]) => {
4763
4780
  // The list reads as "what is running", so a plugin that is off sinks
4764
4781
  // below every one that is on (#745) — even a disabled row with a
@@ -4771,7 +4788,7 @@ export function MarketSection(props: MarketSectionProps) {
4771
4788
  return bUp - aUp
4772
4789
  })
4773
4790
  // eslint-disable-next-line react-hooks/exhaustive-deps -- `updatesLoaded` stands in for `updates`/`updatedNames`: reorder when the check lands, then hold (#631). `effectiveDisabledSet` only re-runs this when a disable state actually changed; the toggled row is the only one that moves.
4774
- }, [isInstalledListActive, displayedInstalled, selfName, updatesLoaded, effectiveDisabledSet])
4791
+ }, [isInstalledListActive, displayedInstalled, selfName, updatesLoaded, effectiveDisabledSet, dependencyOnlyNames])
4775
4792
  const missingRestoreCount = Object.keys(pendingDependencies).filter(name => !installedFiles.includes(name)).length
4776
4793
  // Self-update lives in the header button and the settings card, not this
4777
4794
  // tab's row list (the market itself is filtered out below) — so a pending
@@ -5520,7 +5537,8 @@ export function MarketSection(props: MarketSectionProps) {
5520
5537
  )
5521
5538
 
5522
5539
  /** Installed plugins the market itself cannot group (#60). */
5523
- const groupableNames = Object.keys(installed).filter(name => name !== 'dsh-market' && name !== 'dshmarket')
5540
+ const groupableNames = Object.keys(installed)
5541
+ .filter(name => name !== 'dsh-market' && name !== 'dshmarket' && !dependencyOnlyNames.has(name))
5524
5542
  /** Names already inside some group; everything else shows under "ungrouped". */
5525
5543
  const groupedNames = useMemo(() => new Set(Object.values(groups).flat()), [groups])
5526
5544
  const ungroupedNames = groupableNames.filter(name => !groupedNames.has(name))
@@ -7045,6 +7063,13 @@ export function MarketSection(props: MarketSectionProps) {
7045
7063
  }}
7046
7064
  />
7047
7065
  )}
7066
+ {dependencyOnlyNames.size > 0 && (
7067
+ // The list is complete as a list of PLUGINS; the count keeps it
7068
+ // from looking like packages went missing (#857).
7069
+ <p className={css.groupOrgHint}>
7070
+ {t('installedDependencyNote').replace('{0}', String(dependencyOnlyNames.size))}
7071
+ </p>
7072
+ )}
7048
7073
  </>
7049
7074
  )}
7050
7075
  </div>
@@ -366,6 +366,7 @@ export const zh = {
366
366
  marketUpdate: '更新插件市场',
367
367
  updateAll: '全部更新',
368
368
  heldReleaseNotice: '已安装,但不是最新版:这个 profile 的 minimumReleaseAge 挡住了 {0},安装的是 {1}。',
369
+ installedDependencyNote: '另有 {0} 个包是插件带进来的依赖,不是插件,未列在上方。',
369
370
  heldReleaseAction: '仍安装 {0}',
370
371
  ignoreUpdateNotice: '重启前不再提醒',
371
372
  ignoreAllUpdateNotices: '重启前都不再提醒',
@@ -1096,6 +1097,7 @@ export const en: Record<MarketKey, string> = {
1096
1097
  marketUpdate: 'Update the plugin market',
1097
1098
  updateAll: 'Update all',
1098
1099
  heldReleaseNotice: 'Installed, but not the newest: this profile\'s minimumReleaseAge held {0} back, so {1} was installed.',
1100
+ installedDependencyNote: '{0} more packages came in as dependencies of plugins — they are not plugins, so they are not listed above.',
1099
1101
  heldReleaseAction: 'Install {0} anyway',
1100
1102
  ignoreUpdateNotice: "Don't remind until restart",
1101
1103
  ignoreAllUpdateNotices: "Don't remind any until restart",
package/src/install.ts CHANGED
@@ -334,10 +334,16 @@ export async function withHoistRecovery(
334
334
  // no options from us — but the classified message above says the market
335
335
  // retried. Say that it did not, or the user reads a retry that never
336
336
  // happened and looks for the wrong cause.
337
- result = {
338
- ...result,
339
- stderr: `${result.stderr}\n\n这台宿主(官方桌面端)自己执行安装,不接受市场附加的 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
- }
337
+ //
338
+ // The wait on a fresh release is the one of these where "no retry" leaves
339
+ // the user with nothing to do at all: the plugin IS published, pnpm just
340
+ // will not take it yet, and the door the market would open on a plain
341
+ // `dsh web` host is shut here — so say when the same button starts
342
+ // working, and what to use meanwhile (#852).
343
+ const note = unavailableOption === RELEASE_AGE_OVERRIDE
344
+ ? `这个插件的新版本比本 profile 的 minimumReleaseAge(新版本等待期)还新,pnpm 因此拒绝了本次改动(改别的插件也一样)。官方桌面端由应用自己执行安装,不接受市场的临时放行参数(${unavailableOption}),所以这里没有自动重试。**等这个版本过了等待期再更新一次即可**;想现在就更新,可以在普通的 dsh web 里装。 / The plugin's new version is younger than this profile's minimumReleaseAge, so pnpm refused this change (a change to another plugin too). The official desktop app runs the install itself and takes no one-shot bypass from the market (${unavailableOption}), so no automatic retry happens here. **Update again once the version is past the wait**; to update right now, install from plain dsh web.`
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
+ result = { ...result, stderr: `${result.stderr}\n\n${note}` }
341
347
  }
342
348
  return result
343
349
  }
@@ -432,7 +438,25 @@ export function failureDetail(result: InstallResult, limit = 300): string {
432
438
  const code = result.pnpmErrorCode === undefined ? '' : `${result.pnpmErrorCode}: `
433
439
  return `${code}${result.pnpmError}`.slice(0, limit)
434
440
  }
435
- return (result.stderr || result.stdout).slice(-limit)
441
+ const text = (result.stderr || result.stdout).trim()
442
+ if (text.length <= limit) return text
443
+ const tail = text.slice(-limit)
444
+ // Cut at a sentence end instead of mid-word, and say that something was
445
+ // dropped (#852). The market appends its own explanation to pnpm's output,
446
+ // and this tail is catalogued into user-facing sentences — including inside
447
+ // a parenthesis. A blunt cut landed inside the previous sentence and welded
448
+ // the fragment to the next one: the reporter's log read
449
+ // `(is, export the log and report it这台宿主…`, which is not a sentence in
450
+ // either language and says nothing about what to do.
451
+ const sentenceEnd = /[.!?。!?]["')\])]?[\s\n]+/gu
452
+ const match = sentenceEnd.exec(tail)
453
+ if (match !== null) {
454
+ const rest = tail.slice(match.index + match[0].length).trim()
455
+ // A boundary so near the end would leave a word or two; the raw tail is
456
+ // more use than the stub.
457
+ if (rest.length >= 40) return `… ${rest}`
458
+ }
459
+ return `…${tail.trim()}`
436
460
  }
437
461
 
438
462
  /**