dsh-m 0.5.0 → 0.6.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/lib/client.js CHANGED
@@ -1038,7 +1038,7 @@ function lookup(key, params) {
1038
1038
  var CATEGORIES2 = ["market", "tools", "ui", "search", "other"];
1039
1039
  var CSS = `
1040
1040
  .dshm-overlay{position:fixed;inset:0;z-index:2147483000;background:var(--dsw-alias-bg-mask-3,rgba(15,23,42,.48));display:flex;align-items:center;justify-content:center;padding:24px 16px;box-sizing:border-box}
1041
- .dshm-panel{width:min(920px,100%);height:min(680px,86vh);display:flex;flex-direction:column;background:var(--dsw-alias-bg-overlay,var(--dsw-alias-bg-layer-3,#fff));border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 18px 48px rgba(2,6,23,.25);overflow:hidden;font-family:inherit;color:var(--dsw-alias-label-primary,inherit)}
1041
+ .dshm-panel{width:min(920px,100%);height:min(680px,86vh);display:flex;flex-direction:column;background:var(--dsw-alias-bg-elevated,var(--dsw-alias-bg-base,#fff));background:color-mix(in srgb,var(--dsw-alias-bg-base,#fff) 86%,transparent);backdrop-filter:blur(14px) saturate(1.3);-webkit-backdrop-filter:blur(14px) saturate(1.3);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 18px 48px rgba(2,6,23,.25);overflow:hidden;font-family:inherit;color:var(--dsw-alias-label-primary,inherit)}
1042
1042
  .dshm-head{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb)}
1043
1043
  .dshm-title{font-weight:700;font-size:15px;margin-right:6px}
1044
1044
  .dshm-seg{display:inline-flex;align-items:center;gap:2px;padding:2px;border:1px solid var(--dsw-alias-border-l2,#e2e4e8);border-radius:9px;background:var(--dsw-alias-bg-layer-1,#f5f6f8)}
@@ -1139,7 +1139,7 @@ var CSS = `
1139
1139
  .dshm-dshchip{position:relative;display:inline-flex;align-items:center;gap:4px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#f5f6f8);color:var(--dsw-alias-label-secondary,#4b5563);border-radius:999px;padding:4px 10px;font:11px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;cursor:pointer;white-space:nowrap;flex:none}
1140
1140
  .dshm-dshchip:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06));color:var(--dsw-alias-label-primary,inherit)}
1141
1141
  .dshm-dshchip-v{font-weight:600;color:var(--dsw-alias-label-primary,inherit)}
1142
- .dshm-dshchip-tip{position:absolute;top:calc(100% + 7px);right:0;visibility:hidden;opacity:0;transition:opacity .12s;z-index:60;background:var(--dsw-alias-bg-overlay,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:9px;box-shadow:var(--dsw-shadow-lv1,0 2px 8px rgb(20 24 32 / 8%));padding:7px 10px;text-align:left;pointer-events:none}
1142
+ .dshm-dshchip-tip{position:absolute;top:calc(100% + 7px);right:0;visibility:hidden;opacity:0;transition:opacity .12s;z-index:60;background:var(--dsw-alias-bg-elevated,var(--dsw-alias-bg-base,#fff));background:color-mix(in srgb,var(--dsw-alias-bg-base,#fff) 86%,transparent);backdrop-filter:blur(14px) saturate(1.3);-webkit-backdrop-filter:blur(14px) saturate(1.3);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:9px;box-shadow:var(--dsw-shadow-lv1,0 2px 8px rgb(20 24 32 / 8%));padding:7px 10px;text-align:left;pointer-events:none}
1143
1143
  .dshm-dshchip:hover .dshm-dshchip-tip,.dshm-dshchip:focus-visible .dshm-dshchip-tip{visibility:visible;opacity:1}
1144
1144
  .dshm-dshchip-tiprow{font:12px/16px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-weight:600;color:var(--dsw-alias-label-primary,inherit)}
1145
1145
  .dshm-dshchip-tipsub{display:block;margin-top:2px;font-size:10px;line-height:14px;color:var(--dsw-alias-label-caption,#9ca3af)}
@@ -1157,7 +1157,7 @@ var CSS = `
1157
1157
  .dshm-switch-slider{position:absolute;left:2px;top:2px;width:15px;height:15px;border-radius:50%;background:#fff;transition:left .15s;box-shadow:0 1px 2px rgba(0,0,0,.25)}
1158
1158
  .dshm-switch.on .dshm-switch-slider{left:17px}
1159
1159
  .dshm-compat-overlay{position:fixed;inset:0;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;z-index:60}
1160
- .dshm-compat-dialog{max-width:420px;width:calc(100% - 32px);background:var(--dsw-alias-bg-layer-2,#1c2230);border:1px solid var(--dsw-alias-border-l2,#333b4d);border-radius:12px;padding:16px}
1160
+ .dshm-compat-dialog{max-width:420px;width:calc(100% - 32px);background:var(--dsw-alias-bg-elevated,var(--dsw-alias-bg-base,#1c2230));background:color-mix(in srgb,var(--dsw-alias-bg-base,#1c2230) 86%,transparent);backdrop-filter:blur(14px) saturate(1.3);-webkit-backdrop-filter:blur(14px) saturate(1.3);border:1px solid var(--dsw-alias-border-l2,#333b4d);border-radius:12px;padding:16px}
1161
1161
  .dshm-compat-title{font-weight:600;margin-bottom:8px;color:#f87171}
1162
1162
  .dshm-compat-body{font-size:12px;margin-bottom:6px}
1163
1163
  .dshm-compat-peers{margin:0 0 8px;padding-left:18px;font-size:12px}
@@ -674,7 +674,10 @@ function lockResolutionOf(lockText, pkg) {
674
674
  for (const line of lines) {
675
675
  if (body !== null && /^\S/.test(line))
676
676
  break; // 顶层新段 → 收集结束
677
- const keyMatch = /^ '?(?:[^'\n]*node_modules\/)?([^\s':]+):\s*$/.exec(line);
677
+ // 0.5.1 修复:收尾引号必须可选消耗——pnpm lockfile 对 scoped 包键恒带引号
678
+ //(`'@scope/name@1.2.3':`),旧正则匹配不到任何带引号键,导致 scoped 已装插件
679
+ // 的 lockResolution 恒为 null → derivePrior 误判 restorable:false → 升级被守卫硬拒。
680
+ const keyMatch = /^ '?(?:[^'\n]*node_modules\/)?([^\s':]+)'?:\s*$/.exec(line);
678
681
  if (keyMatch) {
679
682
  if (body)
680
683
  break;
@@ -690,7 +693,9 @@ function lockResolutionOf(lockText, pkg) {
690
693
  return null;
691
694
  const text = body.join('\n');
692
695
  const version = /version:\s*'?([^'\n]+)/.exec(text)?.[1]?.trim();
693
- const integrity = /integrity:\s*(\S+)/.exec(text)?.[1]?.trim();
696
+ // 0.5.1:integrity 捕获排除 flow 结尾(`,`/`}`/引号)——与 npm-integrity.ts parsePackagesSection
697
+ // 同口径;旧 `(\S+)` 会把 `{integrity: sha512-...}` 的收尾 `}` 一起带进 prior 证据。
698
+ const integrity = /integrity:\s*([^,}\s'"]+)/.exec(text)?.[1]?.trim();
694
699
  const commit = /commit:\s*'?([0-9a-f]{40})/.exec(text)?.[1]?.trim();
695
700
  if (!version && !integrity && !commit)
696
701
  return null;
@@ -915,7 +920,8 @@ function guardManualRequired(message, violations) {
915
920
  export async function installEntry(entry, cfg = {}, opts = {}, deps) {
916
921
  return withMutationSession(() => installEntryLocked(entry, cfg, opts, deps));
917
922
  }
918
- /** installEntry 主体(session 区间内;installEntry 是唯一外部入口——upgradePlugin 复用本 helper)。 */
923
+ /** installEntry 主体(session 区间内)。入口约定:installFromRegistry 走 installEntry(获取 session);
924
+ * upgradePlugin 已持有 session,必须直调本函数——二次获取 session 会自死锁(session 非重入)。 */
919
925
  async function installEntryLocked(entry, cfg = {}, opts = {}, deps) {
920
926
  const timeoutMs = cfg.timeoutMs ?? 20_000;
921
927
  const profileDir = deps?.transaction?.profileDir ?? webProfileDir();
@@ -936,7 +942,7 @@ async function installEntryLocked(entry, cfg = {}, opts = {}, deps) {
936
942
  throw new Error(`prior 关联歧义:${prior.reason};拒绝安装(fail-closed)`);
937
943
  }
938
944
  if (prior.kind === 'dependency' && !prior.state.restorable) {
939
- throw guardManualRequired(`「${pkg}」的原安装形态(${prior.state.sourceKind === 'other' ? 'link/file 等非 registry 来源' : '缺少 integrity 的 npm 依赖'})无法自动回退,请先手动处理(卸载或修复后重试)`, [{ pkg, code: 'NO_DSH_MARKER', detail: `prior restorable:false(sourceKind=${prior.state.sourceKind})` }]);
945
+ throw guardManualRequired(`「${pkg}」的原安装形态(${prior.state.sourceKind === 'other' ? 'link/file 等非 registry 来源' : 'pnpm-lock.yaml 未解析出该依赖的 integrity(键缺失或为别名/URL 等非 registry 写法)'})无法自动回退,请先手动处理(卸载或修复后重试)`, [{ pkg, code: 'NO_DSH_MARKER', detail: `prior restorable:false(sourceKind=${prior.state.sourceKind})` }]);
940
946
  }
941
947
  // npm:无论 latest 还是用户指定 exact,都先读取该精确版本的 dist metadata(事务外解析)
942
948
  let version;
@@ -1051,7 +1057,7 @@ async function installEntryLocked(entry, cfg = {}, opts = {}, deps) {
1051
1057
  throw new Error(`prior 关联歧义:${candidatePrior.reason};拒绝安装(fail-closed)`);
1052
1058
  }
1053
1059
  if (candidatePrior.kind === 'dependency' && !candidatePrior.state.restorable) {
1054
- throw guardManualRequired(`「${candidate}」的原安装形态(link/file 等非 registry 来源)无法自动回退,请先手动处理(卸载或修复后重试)`, [{ pkg: candidate, code: 'NO_DSH_MARKER', detail: 'candidate 命中 restorable:false prior(link/file)' }]);
1060
+ throw guardManualRequired(`「${candidate}」的原安装形态(pnpm-lock.yaml 未解析出该依赖的可回退依据——integrity/commit 缺失,或 link/file/别名等非 registry 写法)无法自动回退,请先手动处理(卸载或修复后重试)`, [{ pkg: candidate, code: 'NO_DSH_MARKER', detail: 'candidate 命中 restorable:false prior(lockfile 依据缺失)' }]);
1055
1061
  }
1056
1062
  const result = await runProfileTransaction({ kind: 'install-github', repo: entry.github, sha, tag, signal: opts.signal }, deps?.transaction ?? {});
1057
1063
  if (!result.ok)
@@ -1168,12 +1174,33 @@ async function upgradePluginLocked(pkg, cfg = {}, opts = {}, deps) {
1168
1174
  const target = installed.find((it) => it.pkg === pkg);
1169
1175
  if (!target)
1170
1176
  throw new Error(`web profile 未安装该插件: ${pkg}`);
1171
- const entry = loaded.registry.plugins.find((e) => matchInstalledByEntry(e, [target]));
1177
+ let entry = loaded.registry.plugins.find((e) => matchInstalledByEntry(e, [target]));
1178
+ // 0.5.1:合并市场安装的社区条目同样可升级——主清单 miss 时查社区目录
1179
+ if (!entry)
1180
+ entry = await findCommunityUpgradeEntry(target, cfg, opts, deps);
1172
1181
  if (!entry)
1173
1182
  throw new Error(`「${pkg}」不是经 dsh-m 收录的插件;直接升级请用 dsh plugin update 或先在 registry 收录它`);
1174
- const result = await installEntry(entry, cfg, opts, deps);
1183
+ // 0.5.1 修复:直调 installEntryLocked——upgradePlugin 已在 mutation session 区间内,
1184
+ // 再经 installEntry 二次获取 session 会自死锁(session 非重入,见 withMutationSession 契约)。
1185
+ const result = await installEntryLocked(entry, cfg, opts, deps);
1175
1186
  return { ...result, fromVersion: target.version };
1176
1187
  }
1188
+ /**
1189
+ * 社区条目升级查找(0.5.1):主清单 miss 时按同一 matchInstalledByEntry 语义查社区目录。
1190
+ * 社区清单未启用/不可用/加载异常/未命中 → undefined,由调用方统一报「不是经 dsh-m 收录」。
1191
+ */
1192
+ async function findCommunityUpgradeEntry(target, cfg, opts, deps) {
1193
+ try {
1194
+ const loaded = await (deps?.fetchCommunityCatalog ?? defaultFetchCommunityCatalog)(cfg, { namespace: opts.namespace ?? 'host', signal: opts.signal });
1195
+ if (!loaded.catalog)
1196
+ return undefined;
1197
+ const adapted = adaptCommunityCatalog(loaded.catalog);
1198
+ return adapted.entries.find((e) => matchInstalledByEntry(e, [target]));
1199
+ }
1200
+ catch {
1201
+ return undefined;
1202
+ }
1203
+ }
1177
1204
  /** 自升级(0.5.0 收编:host-api 直调事务的旁路封死):npmLatest + integrity fail-closed + installEntryLocked(session/守卫经 installEntry)。 */
1178
1205
  export async function selfUpgrade(pkgName, currentVersion, cfg = {}, opts = {}, deps) {
1179
1206
  return withMutationSession(async () => {
package/lib/tools.js CHANGED
@@ -337,6 +337,7 @@ export function registerTools(ctx, cfg, deps = {}) {
337
337
  description: 'Upgrade an installed DSH plugin to the latest version (npm 拉最新精确版 / github 重新锁 HEAD)。pkg 来自 dshm_list 或 dshm_outdated。用户确认升级哪一个之后再调用。After success, tell the user it needs a restart, and offer dshm_restart.',
338
338
  parameters: {
339
339
  pkg: { type: 'string', required: true, description: '包名 from dshm_list / dshm_outdated' },
340
+ force: { type: 'boolean', description: 'peer 兼容预检不通过且用户已确认风险后置 true(等价 forceIncompatible,跳过 @deepseek-ai/dsh(-*) peer 拦截);守卫拦截(guard)无 force 通道,不受此参数影响。' },
340
341
  },
341
342
  output: {
342
343
  schema: { type: 'object', additionalProperties: true },
@@ -355,8 +356,9 @@ export function registerTools(ctx, cfg, deps = {}) {
355
356
  const target = String(args.pkg || '').trim();
356
357
  if (!target)
357
358
  throw new Error('缺少 pkg');
359
+ const force = args.force === true;
358
360
  try {
359
- return cloneJson(await m.upgradePlugin(target, cfg, { namespace: 'host' }));
361
+ return cloneJson(await m.upgradePlugin(target, cfg, { forceIncompatible: force, namespace: 'host' }));
360
362
  }
361
363
  catch (err) {
362
364
  if (err instanceof InstallGuardError) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-m",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "可自定义收录清单的 DSH 插件市场:收录、安装、卸载、升级一站完成,附 agent 工具与 dshm CLI。",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/registry.json CHANGED
@@ -138,6 +138,25 @@
138
138
  "github": "iasiv5/dsh-onetree-log",
139
139
  "homepage": "https://github.com/iasiv5/dsh-onetree-log"
140
140
  },
141
+ {
142
+ "id": "dsh-quota-watch",
143
+ "name": "DSH Quota Watch",
144
+ "description": "侧栏额度监控卡片:显示 GLM Coding Plan 窗口与 Copilot 额度快照,凭据宿主解析,不留存历史;已适配 0.1.7-rc.2,详见仓库。",
145
+ "category": "tools",
146
+ "tags": [
147
+ "配额",
148
+ "监控",
149
+ "GLM",
150
+ "Copilot"
151
+ ],
152
+ "verified": [
153
+ "0.1.7-rc.2"
154
+ ],
155
+ "source": "npm",
156
+ "npm": "@iasiv5/dsh-quota-watch",
157
+ "github": "iasiv5/dsh-quota-watch",
158
+ "homepage": "https://github.com/iasiv5/dsh-quota-watch"
159
+ },
141
160
  {
142
161
  "id": "dsh-better-sidebar",
143
162
  "name": "DSH Better Sidebar",