pi-dynamic-help 0.2.1 → 0.2.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.2
4
+
5
+ ### 安全修复
6
+ - **MCP 信息泄漏**: 移除 `/help` 输出中暴露的完整 MCP 服务器 URL 和命令路径,改为显示 "HTTP 服务器" 或 "stdio 服务器"
7
+ - **内置命令重复**: 修复 `/model`、`/help` 等内置命令在列表中显示两次的问题,添加去重逻辑
8
+ - **保存失败提示**: `pin`/`unpin`/`refresh` 命令现在检查保存结果,失败时回滚状态并显示错误信息,不再谎报成功
9
+
10
+ ### 测试增强
11
+ - 添加 MCP URL 消毒测试
12
+ - 添加内置命令去重测试
13
+ - 添加 pin/unpin 保存失败回滚测试
14
+
3
15
  ## 0.2.1
4
16
 
5
17
  - 移除 `/help` 输出中硬编码的 MCP 用法示例(包含特定服务器名 chrome-devtools/screenshot)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-dynamic-help",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Dynamic /help dashboard for Pi commands, tools, MCP servers, and packages.",
5
5
  "type": "module",
6
6
  "keywords": [
package/src/extension.ts CHANGED
@@ -84,9 +84,10 @@ export function registerHelpExtension(pi: ExtensionAPI): void {
84
84
  return index;
85
85
  }
86
86
 
87
- function persist(ctx: ExtensionContext): void {
87
+ function persist(ctx: ExtensionContext): { ok: boolean; warning?: string } {
88
88
  const result = saveState(paths, state);
89
89
  notifySaveWarning(ctx, result.warning);
90
+ return result;
90
91
  }
91
92
 
92
93
  pi.registerCommand("help", {
@@ -101,8 +102,12 @@ export function registerHelpExtension(pi: ExtensionAPI): void {
101
102
 
102
103
  if (subcommand === "refresh") {
103
104
  rebuildIndex(ctx);
104
- persist(ctx);
105
- ctx.ui.notify("帮助索引已刷新", "info");
105
+ const result = persist(ctx);
106
+ if (result.ok) {
107
+ ctx.ui.notify("帮助索引已刷新", "info");
108
+ } else {
109
+ ctx.ui.notify(`帮助索引刷新失败:${result.warning ?? "保存状态失败"}`, "error");
110
+ }
106
111
  return;
107
112
  }
108
113
 
@@ -116,10 +121,16 @@ export function registerHelpExtension(pi: ExtensionAPI): void {
116
121
  }
117
122
 
118
123
  const target = matches[0];
124
+ const wasPinned = target.pinned;
119
125
  target.pinned = subcommand === "pin";
120
126
  target.lastSeenAt = Date.now();
121
- persist(ctx);
122
- ctx.ui.notify(`${subcommand === "pin" ? "已固定" : "已取消固定"}:${target.displayName ?? target.name} (${target.kind} · ${target.sourceLabel})`, "info");
127
+ const result = persist(ctx);
128
+ if (result.ok) {
129
+ ctx.ui.notify(`${subcommand === "pin" ? "已固定" : "已取消固定"}:${target.displayName ?? target.name} (${target.kind} · ${target.sourceLabel})`, "info");
130
+ } else {
131
+ target.pinned = wasPinned; // rollback
132
+ ctx.ui.notify(`${subcommand === "pin" ? "固定" : "取消固定"}失败:${result.warning ?? "保存状态失败"}`, "error");
133
+ }
123
134
  return;
124
135
  }
125
136
 
@@ -103,22 +103,26 @@ export function buildResourceIndex(input: ResourceRuntimeInput): ResourceIndex {
103
103
  for (const server of input.mcpServers) mergedMcp.set(server.name, server);
104
104
 
105
105
  const commandItems: IndexedItem[] = [];
106
+ const seenCommandKeys = new Set<string>();
106
107
  for (const command of commands) {
107
108
  const sourceRef = compactSourceRef(command.sourceInfo.source ?? command.sourceInfo.origin ?? command.sourceInfo.scope);
108
109
  const base = itemBase("command", command.name, sourceRef);
109
- commandItems.push(upsertItem(state, {
110
+ const item = upsertItem(state, {
110
111
  ...base,
111
112
  description: command.description,
112
113
  sourceLabel: sourceLabelForCommand(command.sourceInfo),
113
114
  scope: command.sourceInfo.scope,
114
115
  packageName: command.sourceInfo.source,
115
116
  lastSeenAt: now,
116
- }));
117
+ });
118
+ commandItems.push(item);
119
+ seenCommandKeys.add(item.key);
117
120
  }
118
121
 
119
122
  for (const builtin of BUILTIN_COMMANDS) {
120
123
  const sourceRef = "builtin";
121
124
  const base = itemBase("command", builtin.name, sourceRef);
125
+ if (seenCommandKeys.has(base.key)) continue;
122
126
  commandItems.push(upsertItem(state, {
123
127
  ...base,
124
128
  description: builtin.description,
@@ -172,10 +176,10 @@ export function buildResourceIndex(input: ResourceRuntimeInput): ResourceIndex {
172
176
  const base = itemBase("mcp", server.name, sourceRef);
173
177
  mcpItems.push(upsertItem(state, {
174
178
  ...base,
175
- description: server.url ? server.url : server.command ? server.command : "MCP 服务器",
179
+ description: server.mode === "http" ? "HTTP 服务器" : "stdio 服务器",
176
180
  sourceLabel: sourceLabelForMcp(server),
177
181
  scope: "user",
178
- packageName: server.command ?? server.url,
182
+ packageName: undefined,
179
183
  lastSeenAt: now,
180
184
  }));
181
185
  }