dshmarket 1.66.33 → 1.66.35

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
@@ -9587,12 +9587,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
9587
9587
  const row = recentRowRef.current;
9588
9588
  if (row === null || typeof ResizeObserver === "undefined") return;
9589
9589
  const observer = new ResizeObserver(([entry]) => {
9590
- if (entry !== void 0) setRecentColumns(Math.max(1, Math.floor((entry.contentRect.width + 10) / 290)));
9590
+ if (entry !== void 0) setRecentColumns(Math.min(2, Math.max(1, Math.floor((entry.contentRect.width + 10) / 290))));
9591
9591
  });
9592
9592
  observer.observe(row);
9593
9593
  return () => observer.disconnect();
9594
9594
  }, [hasRecent, loadError]);
9595
- const recentPlugins = recentlyAdded.slice(0, recentColumns);
9595
+ const recentPlugins = recentlyAdded.slice(0, recentColumns * 2);
9596
9596
  const { currentPage, totalPages, pageSize, goToPage, changePageSize } = usePagination(plugins.length, [
9597
9597
  q,
9598
9598
  cat,
@@ -13188,7 +13188,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
13188
13188
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13189
13189
  ref: recentRowRef,
13190
13190
  className: Market_module_css_default.recentRow,
13191
- style: { gridTemplateColumns: `repeat(${recentPlugins.length}, minmax(0, 1fr))` },
13191
+ style: { gridTemplateColumns: `repeat(${recentColumns}, minmax(0, 1fr))` },
13192
13192
  children: recentPlugins.map((plugin) => pluginCard(plugin, `recent:${plugin.url}`))
13193
13193
  })]
13194
13194
  }),
@@ -18,8 +18,48 @@ function detail(error) {
18
18
  return error.message;
19
19
  if (error === undefined)
20
20
  return 'plugin manager did not explain the failure';
21
+ const code = managementCode(error);
22
+ const copy = code === undefined ? undefined : REFUSAL_COPY[code];
23
+ // The code stays on the line: it is what the log and the issue report carry.
24
+ if (copy !== undefined)
25
+ return `${copy.zh} / ${copy.en} (${code})`;
21
26
  return JSON.stringify(error);
22
27
  }
28
+ /**
29
+ * What the manager's refusal means, in the user's terms.
30
+ *
31
+ * These codes are the manager's localization keys, not sentences; they used to
32
+ * reach the page as `{"code":"already-installed"}` and, on the update route,
33
+ * wrapped in "restoration of the previous build could not be verified" — alarm
34
+ * about a profile that was never touched (#897). The bilingual half is picked
35
+ * by the same `localizeBilingual` every other server message goes through.
36
+ */
37
+ const REFUSAL_COPY = {
38
+ 'already-installed': {
39
+ zh: '桌面端认为这个包已经装在 profile 里,因此在运行 pnpm 之前就拒绝了这次安装,profile 没有任何改动',
40
+ en: 'the desktop app treats this package as already installed and refused the install before pnpm ran, so the profile is unchanged',
41
+ },
42
+ 'not-removable': {
43
+ zh: '这个插件由桌面端自己管理,市场不能移除它',
44
+ en: 'the desktop app manages this plugin itself, so the market cannot remove it',
45
+ },
46
+ 'bundle-in-use': {
47
+ zh: '这个插件正在使用中,桌面端不允许现在移除',
48
+ en: 'the plugin is in use, and the desktop app will not remove it right now',
49
+ },
50
+ 'stop-profile': {
51
+ zh: '要移除它得先停止这个 profile',
52
+ en: 'removing it requires stopping this profile first',
53
+ },
54
+ 'management-required': {
55
+ zh: '这个 profile 由桌面端自己管理,市场不能直接改动',
56
+ en: 'this profile is managed by the desktop app itself, so the market cannot change it',
57
+ },
58
+ 'incompatible-version': {
59
+ zh: '这个版本与当前 DSH 不兼容,宿主拒绝安装',
60
+ en: 'this version is not compatible with the running DSH, so the host refused it',
61
+ },
62
+ };
23
63
  /** The manager's own refusal code, whether it was returned or thrown. */
24
64
  function managementCode(error) {
25
65
  const code = error?.code;
@@ -172,11 +212,14 @@ _profileDirectory) {
172
212
  stdout: output,
173
213
  stderr: message,
174
214
  cancelled: current.cancelled || result.application === 'cancelled',
215
+ // No package result means the manager stopped before it ran pnpm: the
216
+ // profile is untouched, and the route must not try to "restore" it.
217
+ wroteNothing: !ok && result.packageResult === undefined,
175
218
  };
176
219
  }, (error) => {
177
220
  const message = detail(error);
178
221
  progress.error = message;
179
- return failure(message, 127);
222
+ return { ...failure(message, 127), wroteNothing: managementCode(error) !== undefined };
180
223
  }).finally(() => {
181
224
  progress.active = false;
182
225
  progress.cancelling = false;
package/lib/routes.js CHANGED
@@ -4597,7 +4597,13 @@ export function mountMarketRoutes(host, config, commandRuntime, agentsLookup, ho
4597
4597
  + (dropped ? '' : ' (nothing to drop: the profile did not declare it under either key)'));
4598
4598
  };
4599
4599
  if ((result.exitCode !== 0 || result.timedOut) && !cancelled && result.busy !== true
4600
- && !pnpmNeverStarted(result)) {
4600
+ && !pnpmNeverStarted(result)
4601
+ // The host refused before running pnpm (#897): nothing was
4602
+ // written, so there is nothing to restore — and the "could not
4603
+ // verify restoration, inspect this profile" warning this block
4604
+ // raises on a failed rollback would be alarm about an untouched
4605
+ // profile.
4606
+ && result.wroteNothing !== true) {
4601
4607
  if (pnpmBlockedByOpenFiles(result)) {
4602
4608
  const kept = keepLockedBuild();
4603
4609
  rollbackOk = kept.ok;
@@ -4941,7 +4947,13 @@ export function mountMarketRoutes(host, config, commandRuntime, agentsLookup, ho
4941
4947
  // Last, so the specific ones above keep the floor they had: this
4942
4948
  // only fills the case where every named branch said nothing and
4943
4949
  // the user would otherwise get pnpm's stack (#848).
4944
- error: versionFailureError ?? renamedError ?? trialError ?? brokenEntryError ?? hardFailureRollbackError ?? staleError ?? pnpmFailureMessage(result) ?? undefined,
4950
+ // The host's own refusal sentence (#897): when it stopped
4951
+ // before running pnpm there is no classifier for its words and
4952
+ // no rollback behind it, so without this the answer carried no
4953
+ // reason at all.
4954
+ error: versionFailureError ?? renamedError ?? trialError ?? brokenEntryError ?? hardFailureRollbackError ?? staleError
4955
+ ?? (result.wroteNothing === true && result.stderr.trim() !== '' ? result.stderr.trim() : undefined)
4956
+ ?? pnpmFailureMessage(result) ?? undefined,
4945
4957
  exitCode: result.exitCode,
4946
4958
  timedOut: result.timedOut,
4947
4959
  stdout: result.stdout,
@@ -161,6 +161,17 @@ export interface InstallResult {
161
161
  cancelled: boolean;
162
162
  /** Desktop's generation-wide package-operation gate rejected the start. */
163
163
  busy?: boolean;
164
+ /**
165
+ * The host refused before it ran the package manager, so the profile is
166
+ * exactly as it was.
167
+ *
168
+ * The update route answers a failed run by reinstalling the previous build
169
+ * and, when that cannot be verified, tells the user to inspect a profile
170
+ * nothing touched. Desktop's manager has refusals of this kind of its own —
171
+ * it declines a spec it considers already installed before pnpm starts
172
+ * (#897) — and their only true account is "nothing happened".
173
+ */
174
+ wroteNothing?: boolean;
164
175
  /** Package names pnpm reported as having ignored build scripts (ndjson). */
165
176
  ignoredBuilds?: string[];
166
177
  /**
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.33",
4
+ "version": "1.66.35",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",
@@ -3074,18 +3074,22 @@ export function MarketSection(props: MarketSectionProps) {
3074
3074
  // eslint-disable-next-line react-hooks/exhaustive-deps
3075
3075
  [data, tab, q, cat, timeRange, lang, hostCompatibility, compatibleWithHost, blockedNameSet, installed, repoIdentities, repoHints])
3076
3076
  const recentRowRef = useRef<HTMLDivElement>(null)
3077
+ // Two per row, like the grid below it: three across read as a different
3078
+ // list rather than the same one (and left an odd one out at any width).
3079
+ // Two rows of two keeps the block square and the cards the size of the
3080
+ // cards underneath.
3077
3081
  const [recentColumns, setRecentColumns] = useState(1)
3078
3082
  const hasRecent = recentlyAdded.length > 0
3079
3083
  useLayoutEffect(() => {
3080
3084
  const row = recentRowRef.current
3081
3085
  if (row === null || typeof ResizeObserver === 'undefined') return
3082
3086
  const observer = new ResizeObserver(([entry]) => {
3083
- if (entry !== undefined) setRecentColumns(Math.max(1, Math.floor((entry.contentRect.width + 10) / 290)))
3087
+ if (entry !== undefined) setRecentColumns(Math.min(2, Math.max(1, Math.floor((entry.contentRect.width + 10) / 290))))
3084
3088
  })
3085
3089
  observer.observe(row)
3086
3090
  return () => observer.disconnect()
3087
3091
  }, [hasRecent, loadError])
3088
- const recentPlugins = recentlyAdded.slice(0, recentColumns)
3092
+ const recentPlugins = recentlyAdded.slice(0, recentColumns * 2)
3089
3093
  const { currentPage, totalPages, pageSize, goToPage, changePageSize } =
3090
3094
  usePagination(plugins.length, [q, cat, sortField, sortDir, timeRange, compatibleWithHost], scrollToTop)
3091
3095
  const pagePlugins = plugins.slice((currentPage - 1) * pageSize, currentPage * pageSize)
@@ -6490,7 +6494,7 @@ export function MarketSection(props: MarketSectionProps) {
6490
6494
  {hasRecent && (
6491
6495
  <section aria-labelledby="dshm-recently-added">
6492
6496
  <h3 id="dshm-recently-added" className={css.favoritesSectionHead}>{t('recentlyAdded')}</h3>
6493
- <div ref={recentRowRef} className={css.recentRow} style={{ gridTemplateColumns: `repeat(${recentPlugins.length}, minmax(0, 1fr))` }}>
6497
+ <div ref={recentRowRef} className={css.recentRow} style={{ gridTemplateColumns: `repeat(${recentColumns}, minmax(0, 1fr))` }}>
6494
6498
  {recentPlugins.map(plugin => pluginCard(plugin, `recent:${plugin.url}`))}
6495
6499
  </div>
6496
6500
  </section>
package/src/dsh-cli.ts CHANGED
@@ -500,6 +500,17 @@ export interface InstallResult {
500
500
  cancelled: boolean
501
501
  /** Desktop's generation-wide package-operation gate rejected the start. */
502
502
  busy?: boolean
503
+ /**
504
+ * The host refused before it ran the package manager, so the profile is
505
+ * exactly as it was.
506
+ *
507
+ * The update route answers a failed run by reinstalling the previous build
508
+ * and, when that cannot be verified, tells the user to inspect a profile
509
+ * nothing touched. Desktop's manager has refusals of this kind of its own —
510
+ * it declines a spec it considers already installed before pnpm starts
511
+ * (#897) — and their only true account is "nothing happened".
512
+ */
513
+ wroteNothing?: boolean
503
514
  /** Package names pnpm reported as having ignored build scripts (ndjson). */
504
515
  ignoredBuilds?: string[]
505
516
  /**
@@ -31,9 +31,49 @@ function detail(error: unknown): string {
31
31
  if (typeof error === 'string') return error
32
32
  if (error instanceof Error) return error.message
33
33
  if (error === undefined) return 'plugin manager did not explain the failure'
34
+ const code = managementCode(error)
35
+ const copy = code === undefined ? undefined : REFUSAL_COPY[code]
36
+ // The code stays on the line: it is what the log and the issue report carry.
37
+ if (copy !== undefined) return `${copy.zh} / ${copy.en} (${code})`
34
38
  return JSON.stringify(error)
35
39
  }
36
40
 
41
+ /**
42
+ * What the manager's refusal means, in the user's terms.
43
+ *
44
+ * These codes are the manager's localization keys, not sentences; they used to
45
+ * reach the page as `{"code":"already-installed"}` and, on the update route,
46
+ * wrapped in "restoration of the previous build could not be verified" — alarm
47
+ * about a profile that was never touched (#897). The bilingual half is picked
48
+ * by the same `localizeBilingual` every other server message goes through.
49
+ */
50
+ const REFUSAL_COPY: Record<string, { zh: string; en: string }> = {
51
+ 'already-installed': {
52
+ zh: '桌面端认为这个包已经装在 profile 里,因此在运行 pnpm 之前就拒绝了这次安装,profile 没有任何改动',
53
+ en: 'the desktop app treats this package as already installed and refused the install before pnpm ran, so the profile is unchanged',
54
+ },
55
+ 'not-removable': {
56
+ zh: '这个插件由桌面端自己管理,市场不能移除它',
57
+ en: 'the desktop app manages this plugin itself, so the market cannot remove it',
58
+ },
59
+ 'bundle-in-use': {
60
+ zh: '这个插件正在使用中,桌面端不允许现在移除',
61
+ en: 'the plugin is in use, and the desktop app will not remove it right now',
62
+ },
63
+ 'stop-profile': {
64
+ zh: '要移除它得先停止这个 profile',
65
+ en: 'removing it requires stopping this profile first',
66
+ },
67
+ 'management-required': {
68
+ zh: '这个 profile 由桌面端自己管理,市场不能直接改动',
69
+ en: 'this profile is managed by the desktop app itself, so the market cannot change it',
70
+ },
71
+ 'incompatible-version': {
72
+ zh: '这个版本与当前 DSH 不兼容,宿主拒绝安装',
73
+ en: 'this version is not compatible with the running DSH, so the host refused it',
74
+ },
75
+ }
76
+
37
77
  /** The manager's own refusal code, whether it was returned or thrown. */
38
78
  function managementCode(error: unknown): string | undefined {
39
79
  const code = (error as { code?: unknown } | null | undefined)?.code
@@ -189,11 +229,14 @@ export function createOfficialDesktopRuntime(
189
229
  stdout: output,
190
230
  stderr: message,
191
231
  cancelled: current.cancelled || result.application === 'cancelled',
232
+ // No package result means the manager stopped before it ran pnpm: the
233
+ // profile is untouched, and the route must not try to "restore" it.
234
+ wroteNothing: !ok && result.packageResult === undefined,
192
235
  }
193
236
  }, (error): InstallResult => {
194
237
  const message = detail(error)
195
238
  progress.error = message
196
- return failure(message, 127)
239
+ return { ...failure(message, 127), wroteNothing: managementCode(error) !== undefined }
197
240
  }).finally(() => {
198
241
  progress.active = false
199
242
  progress.cancelling = false
package/src/routes.ts CHANGED
@@ -4808,7 +4808,13 @@ sendJson(response, 200, { updates })
4808
4808
  + (dropped ? '' : ' (nothing to drop: the profile did not declare it under either key)'))
4809
4809
  }
4810
4810
  if ((result.exitCode !== 0 || result.timedOut) && !cancelled && result.busy !== true
4811
- && !pnpmNeverStarted(result)) {
4811
+ && !pnpmNeverStarted(result)
4812
+ // The host refused before running pnpm (#897): nothing was
4813
+ // written, so there is nothing to restore — and the "could not
4814
+ // verify restoration, inspect this profile" warning this block
4815
+ // raises on a failed rollback would be alarm about an untouched
4816
+ // profile.
4817
+ && result.wroteNothing !== true) {
4812
4818
  if (pnpmBlockedByOpenFiles(result)) {
4813
4819
  const kept = keepLockedBuild()
4814
4820
  rollbackOk = kept.ok
@@ -5170,7 +5176,13 @@ sendJson(response, 200, { updates })
5170
5176
  // Last, so the specific ones above keep the floor they had: this
5171
5177
  // only fills the case where every named branch said nothing and
5172
5178
  // the user would otherwise get pnpm's stack (#848).
5173
- error: versionFailureError ?? renamedError ?? trialError ?? brokenEntryError ?? hardFailureRollbackError ?? staleError ?? pnpmFailureMessage(result) ?? undefined,
5179
+ // The host's own refusal sentence (#897): when it stopped
5180
+ // before running pnpm there is no classifier for its words and
5181
+ // no rollback behind it, so without this the answer carried no
5182
+ // reason at all.
5183
+ error: versionFailureError ?? renamedError ?? trialError ?? brokenEntryError ?? hardFailureRollbackError ?? staleError
5184
+ ?? (result.wroteNothing === true && result.stderr.trim() !== '' ? result.stderr.trim() : undefined)
5185
+ ?? pnpmFailureMessage(result) ?? undefined,
5174
5186
  exitCode: result.exitCode,
5175
5187
  timedOut: result.timedOut,
5176
5188
  stdout: result.stdout,