dsh-toolbox-ui 1.6.5 → 1.6.6

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/README.en.md CHANGED
@@ -43,8 +43,9 @@ See every installed skill's **real scope** and cross-scope shadowing, enable/dis
43
43
  **Skills**
44
44
 
45
45
  - Real scope and cross-scope shadowing: the same name in different scopes is a legal configuration; the panel lists each copy and marks which one wins
46
- - Enable/disable (`.disabled`), remove, read the body and diagnostics — the reason a skill "does not take effect" is right there in the panel
46
+ - Enable/disable (by writing the two invocation fields in the skill file, **without touching the file itself**), remove, read the body and diagnostics — the reason a skill "does not take effect" is right there in the panel
47
47
  - **Model auto-invocation switch**: edits `disable-model-invocation` in `SKILL.md`; turning it off deletes that line (off → on round-trips byte-for-byte). The state always comes from the skill file itself — when it cannot be read the panel says "unknown" instead of guessing
48
+ - **Located by its real position on disk** (never by skill name): skills whose file or directory name differs from the frontmatter `name` can still be read, toggled and removed
48
49
  - Market backed by [skillhub.cn](https://skillhub.cn): publisher verification and security-scan findings are disclosed before install, followed by one **explicit confirmation** (validated server-side)
49
50
 
50
51
  **Shared**
@@ -57,10 +58,10 @@ See every installed skill's **real scope** and cross-scope shadowing, enable/dis
57
58
 
58
59
  ```sh
59
60
  # Recommended: from npm — `dsh plugin update` then upgrades within ^1.x
60
- dsh plugin --profile web add dsh-toolbox-ui@^1.6.5
61
+ dsh plugin --profile web add dsh-toolbox-ui@^1.6.6
61
62
 
62
63
  # Or pin a GitHub release tag (no automatic upgrades; re-add with a new tag)
63
- dsh plugin --profile web add github:Imzl-zl/dsh-toolbox-ui#v1.6.5
64
+ dsh plugin --profile web add github:Imzl-zl/dsh-toolbox-ui#v1.6.6
64
65
  ```
65
66
 
66
67
  Then **restart `dsh web`**. To uninstall (your existing MCP entries stay):
package/README.md CHANGED
@@ -43,8 +43,9 @@
43
43
  **技能面**
44
44
 
45
45
  - 真实作用域与同名遮蔽:同名技能跨作用域是合法配置,面板逐条列出并标出哪一份生效
46
- - 启停(`.disabled`)、删除、读正文与诊断——「技能不生效」的原因直接摆在面板里
46
+ - 启停(写技能文件里的两个调用面字段,**不动文件本身**)、删除、读正文与诊断——「技能不生效」的原因直接摆在面板里
47
47
  - **「模型自动调用」开关**:直接改 `SKILL.md` 的 `disable-model-invocation`,关掉=删掉那一行(开→关能逐字节还原原文);状态一律取自技能文件本身,读不出来就说「未知」而不是猜一个值
48
+ - **定位按磁盘上的真实位置**(不按技能名):文件/目录名与 frontmatter 的 `name` 不一致的技能照样能读、能启停、能删
48
49
  - 市场接 [skillhub.cn](https://skillhub.cn):安装前披露发布方认证与安全扫描结论,并要一次**显式确认**(该确认在服务端校验)
49
50
 
50
51
  **公共**
@@ -57,10 +58,10 @@
57
58
 
58
59
  ```sh
59
60
  # 推荐:npm 安装 —— 之后 `dsh plugin update` 在 ^1.x 内自动升小版本
60
- dsh plugin --profile web add dsh-toolbox-ui@^1.6.5
61
+ dsh plugin --profile web add dsh-toolbox-ui@^1.6.6
61
62
 
62
63
  # 或固定 GitHub release tag(不自动跨版本,升级要换 tag 重新 add)
63
- dsh plugin --profile web add github:Imzl-zl/dsh-toolbox-ui#v1.6.5
64
+ dsh plugin --profile web add github:Imzl-zl/dsh-toolbox-ui#v1.6.6
64
65
  ```
65
66
 
66
67
  装完**重启 `dsh web`**。卸载(不会删除 profile 里已有的 MCP 条目):
package/docs/design.md CHANGED
@@ -349,18 +349,48 @@ document.head.appendChild(tag)
349
349
  一个 contribution 天然支持多 namespace。同理 `SKILLS_CSS` 必须与 `MCP_CSS` **合并成一次**
350
350
  `__injectCss` 调用(那个函数的 id 写死,二次调用会覆写同一 `<style>`)。
351
351
 
352
- ### 技能的「根目录」从哪来
352
+ ### 技能的「位置」从哪来(根 + 磁盘条目)
353
353
 
354
354
  `ctx.skills` **不暴露 `roots()`**(公开面只有 list/get/snapshot/collect/register),而启停与删除
355
- 必须按 `(name, scope, root)` 精确定位。所以根目录从 summary 的 `resourceBase` 反推,判别式是
356
- **`path` 是否恰为 `<base>/SKILL.md`**(目录捆绑)还是 `dirname(path) === base`(扁平文件)。
357
- 推不出来就**不填**——猜错会让启停/删除作用在错误的目录上,是本层最危险的失败模式。
355
+ 必须精确到**一个文件**。所以位置从 summary 的 `resourceBase` 与 `path` 反推——但要分成两件事:
358
356
 
359
- ### 禁用为什么是改名
357
+ - **根**:目录捆绑取 `dirname(resourceBase.path)`,扁平取 `resourceBase.path` 本身。
358
+ 这是宿主给的**展示路径**,也正是写盘要用的那个。
359
+ - **磁盘条目**:目录捆绑取 `resourceBase.path` 的末段(宿主固定构造 `directory = <根>/<条目>`,
360
+ 所以末段就是磁盘目录名);扁平则在根下**扫一遍、按 realpath 匹配宿主报的 `path`** 查出那个名字。
360
361
 
361
- DSH 的技能没有 `disabled` 字段(MCP 有、技能没有),所以停用只能把 `SKILL.md` 改名为
362
- `SKILL.md.disabled`。代价是技能从宿主目录里**消失**(宿主按固定路径读取,读不到就跳过),
363
- 因此「已禁用」必须由本插件**自己扫描** `.disabled` 才能展示与恢复。
362
+ ⚠️ **技能名(frontmatter 的 `name`)不参与定位**。宿主允许磁盘条目名与它不一致,按名字拼路径会让
363
+ 这类技能在面板里所有操作都失败。两个路径字段的**口径也不同**(`resourceBase.path` 是展示路径、
364
+ `path` 是 realpath),链接下必然分叉,所以不能拿它们互相做路径比较。
365
+ 推不出位置就**不填**——猜错会让启停/删除作用在错误的目录上,是本层最危险的失败模式。
366
+
367
+ ### 启停为什么写 frontmatter,而不是改名
368
+
369
+ DSH 的技能没有 `disabled` 字段(MCP 有、技能没有)。但宿主原生的**两个调用面字段**组合起来
370
+ 就是「停用」,而且这正是 VS Code / Copilot 官方文档里叫 “Disabled skills” 的那一档:
371
+
372
+ ```
373
+ disable-model-invocation: true # 模型不自动调用
374
+ user-invocable: false # 你也不能手动调用
375
+ ```
376
+
377
+ 于是「停用」= 写这两个字段,「启用」= 都回到缺省。好处是**结构性的**:
378
+
379
+ - 技能仍在磁盘原处,宿主**照样报告它**(`list` 是调用无关的)→ 面板不必自己扫盘
380
+ → 没有第二真值来源,也就没有「同一身份两行」「链接目录扫不到」那一类缺陷;
381
+ - 不再产生 `SKILL.md.disabled` 这个**任何规范都不认识**的形态。用户的技能常在
382
+ `~/.agents/skills`——Agent Skills 标准的共享位置,VS Code / Codex 都扫它;
383
+ 把它改名等于为了本面板的一个开关,在**别的工具里**把用户的技能弄坏。
384
+
385
+ ⚠️ 代价(写在这里免得下次当成 bug):**「启用」会把两个调用面都恢复成可调用**。
386
+ 用户原本若手工设过 `disable-model-invocation: true`(只想要「仅手动可调用」那一档),
387
+ 一次「停用→启用」往返会重置它。所以详情页把两个面**各自**做成一个独立的可写开关
388
+ (`setModelInvocable` / `setUserInvocable`,共用 `setSkillInvocationFace` 一处实现)——
389
+ 重置了也能在面板上拨回来,不必去编辑文件。「停用」只是「两个都关」这一个组合的快捷动作。
390
+
391
+ ⚠️ 1.6.x 用的是「改名成 `SKILL.md.disabled`」那套自创机制。`scanDisabledSkills` 保留为
392
+ **一次性迁移读取**:把用户磁盘上(我们自己写下的)那些残留列出来并允许恢复,
393
+ 它在下一个大版本可以整体删除——它不是第二个真值来源。
364
394
 
365
395
  ### 许可门禁为什么逐技能判定(2026-10-10 已下线)
366
396
 
@@ -418,8 +448,8 @@ DSH 已经有一个接 skillhub 的第三方插件(`@cocofhu/skillhub`),
418
448
  - `lib/env-expression.js`:受限 `!!js` 表达式的唯一实现(生成 / 校验 / 抽取 / 反转 / 求值)
419
449
  - `lib/import-admission.js`:导入结果的写盘前准入(同作用域 `serverName` 唯一、环境变量引用归一为总值形式)
420
450
  - `lib/mcp-observability.js`:连接状态判定与 mcp-client 日志格式化
421
- - `lib/skills-registry.js`:宿主 `ctx.skills` 的投影(source/作用域/根目录推导)与目录指纹
422
- - `lib/skills-file-ops.js`:技能文件动作(启停 = 改名 `SKILL.md` ↔ `SKILL.md.disabled`、删除、导入、扫描已禁用),按 `(name, scope, root)` 精确定位、找不到即报错不回退
451
+ - `lib/skills-registry.js`:宿主 `ctx.skills` 的投影(source / 作用域 / 根 + 磁盘条目推导)、安装目标能力表、物理位置去重
452
+ - `lib/skills-file-ops.js`:技能文件动作(启停 = 写 frontmatter 两个调用面字段、删除、导入、旧机制残留的迁移读取),按 `(根, 磁盘条目)` 精确定位、找不到即报错不回退
423
453
  - `lib/skills-archive.js`:压缩包安全解包(防 zip slip、拒符号链接、按**实际解出字节**卡限额)
424
454
  - `lib/skills-frontmatter.js`:frontmatter 校验与逐条诊断(判定逐字照抄宿主,宿主只 warn+skip,用户看不到)
425
455
  - `lib/client.js`:响应式 Web UI、Remote 客户端和生命周期清理
@@ -15,14 +15,14 @@
15
15
  推荐用 npm 安装:包里的 `^1.x` 范围让后续 `dsh plugin update` 能自动升级小版本。
16
16
 
17
17
  ```sh
18
- dsh plugin --profile web add dsh-toolbox-ui@^1.6.5
18
+ dsh plugin --profile web add dsh-toolbox-ui@^1.6.6
19
19
  ```
20
20
 
21
21
  也可以固定 GitHub release tag(适合需要锁定某个具体版本时):
22
22
 
23
23
  ```sh
24
24
  # 固定 release tag
25
- dsh plugin --profile web add github:Imzl-zl/dsh-toolbox-ui#v1.6.5
25
+ dsh plugin --profile web add github:Imzl-zl/dsh-toolbox-ui#v1.6.6
26
26
 
27
27
  # 或固定某个 commit
28
28
  dsh plugin --profile web add github:Imzl-zl/dsh-toolbox-ui#<commit>
@@ -73,7 +73,7 @@ dsh plugin --profile web update dsh-toolbox-ui
73
73
 
74
74
  | 安装规格 | `update` 的效果 | 怎么跨版本 |
75
75
  |---|---|---|
76
- | `dsh-toolbox-ui@^1.6.5`(npm,推荐) | 在 `^1.x` 内自动升到最新小版本,不会跨到 2.0.0 | 已是自动;跨大版本显式安装 `@^2` |
76
+ | `dsh-toolbox-ui@^1.6.6`(npm,推荐) | 在 `^1.x` 内自动升到最新小版本,不会跨到 2.0.0 | 已是自动;跨大版本显式安装 `@^2` |
77
77
  | `github:...#v1.4.0`(tag) | 重新解析同一个 tag,**不会动** | 换成新 tag 重新 `add`:`... #<新 tag>` |
78
78
  | `github:...#<commit>` | 同上,固定在 commit | 用新 commit/tag 重新 `add` |
79
79
  | `github:...#main`(分支) | 拉取最新 main | 已是自动;但不推荐生产(无 release 门槛) |
@@ -113,7 +113,7 @@ dsh web # 打开面板点一遍
113
113
  # 3) 提交并推 tag(CI 会校验 tag 与 version 一致,不一致就拒绝发布)
114
114
  git add -A && git commit -m "..."
115
115
  git push origin main
116
- git tag -a v1.6.5 -m "dsh-toolbox-ui 1.6.5" && git push origin v1.6.5
116
+ git tag -a v1.6.6 -m "dsh-toolbox-ui 1.6.6" && git push origin v1.6.6
117
117
  ```
118
118
 
119
119
  推 tag 后 CI 依次做:校验版本 → `pnpm install --frozen-lockfile` → `npm test` → `npm publish`(trusted publishing,无需 token)→ `gh release create`。
@@ -132,7 +132,7 @@ git tag -a v1.6.5 -m "dsh-toolbox-ui 1.6.5" && git push origin v1.6.5
132
132
 
133
133
  ```sh
134
134
  npm publish # 会弹 2FA 挑战;若用 bypass-2FA 的 granular token 则非交互
135
- gh release create v1.6.5 --title "dsh-toolbox-ui v1.6.5" --notes-file <notes>
135
+ gh release create v1.6.6 --title "dsh-toolbox-ui v1.6.6" --notes-file <notes>
136
136
  ```
137
137
 
138
138
  `npm login` 的会话 token 只有 2 小时,超时需要重新登录;classic token 已被 npm 永久吊销,能非交互发布的只剩勾了 **Bypass 2FA** 的 granular access token——但它正在被 npm 收窄(已不能做账号/包管理类操作,且 2027-01 起将失去直接发布能力),所以长期方案是上面的 trusted publishing。
package/docs/skills.md CHANGED
@@ -5,8 +5,8 @@
5
5
  技能能力面解决的是 DSH 自带技能目录**看不到**的那些事:
6
6
 
7
7
  - 技能到底装在哪个作用域(同名技能跨作用域是合法配置,但模型只会收到生效的那一份);
8
- - 哪些技能被**停用**了(改名后宿主目录里直接消失,模型和面板都看不见它)——面板会自行扫 `.disabled` 把它们列出来,并给一键恢复;
9
- - 哪些技能**允许被模型自动调用**,以及就地开关(写的是 `SKILL.md` 的 frontmatter,见[模型自动调用](#允许禁止模型自动调用));
8
+ - 哪些技能被**停用**了,以及一键启停(写的是 `SKILL.md` 的两个调用面字段,见[启用与停用](#启用与停用));
9
+ - 哪些技能**允许被模型自动调用**、哪些**允许你手动调用**,以及就地开关;
10
10
  - 装市场里的技能前,你被如实告知了什么、必须确认什么(披露 + 一次显式确认,不做许可判定)。
11
11
 
12
12
  ⚠️ 一个**还没做**的事:宿主对 frontmatter 非法的技能只 `warn` 后跳过,那种技能目前**不会**出现在面板里(要报它得自己扫各根的技能文件并跑一遍校验,见 TODO row 21)。面板不会假装能看到它——所以遇到这种技能时,面板给出的调用权限是「未知」而不是一个猜的值。
@@ -39,7 +39,7 @@ MCP 面的市场走的是同一个模式的同一种形态(内联网格,不
39
39
  | 500 | `<AGENTS_HOME>/skills` | 全局 |
40
40
  | 600 | 内置(bundled) | **只读**,不可启停/删除 |
41
41
 
42
- 同名技能出现在多个作用域时,面板会标出**哪一份生效、哪一份被遮蔽**——被遮蔽的那份模型收不到,但你确实装了它。生效者按优先级数值**最小**者判定,与它在列表里的位置无关。⚠️ 边界:宿主按名字只报告**生效**的那一份,所以落选副本只有在以 `SKILL.md.disabled` 形态留下时才看得见;两份都启用时,落选那份目前不在面板数据里(要报它得自己扫各根,见 TODO row 21 旁边的同名副本扫描)。
42
+ 同名技能出现在多个作用域时,面板会标出**哪一份生效、哪一份被遮蔽**——被遮蔽的那份模型收不到,但你确实装了它。生效者按优先级数值**最小**者判定,与它在列表里的位置无关。⚠️ 边界:宿主按名字只报告**生效**的那一份,所以落选的副本只有在**两个调用面都关掉**(= 已停用,那种条目照样会出现在宿主目录里)时才看得见;两份都可调用时,落选那份目前不在面板数据里(要报它得自己扫各根,见 TODO row 21 旁边的同名副本扫描)。
43
43
 
44
44
  ### 同一个目录被两处指到时
45
45
 
@@ -51,10 +51,27 @@ MCP 面的市场走的是同一个模式的同一种形态(内联网格,不
51
51
 
52
52
  ## 启用与停用
53
53
 
54
- DSH 的技能没有「disabled」字段(MCP 有,技能没有),所以启停的做法是把 `SKILL.md` 改名为 `SKILL.md.disabled`:
54
+ DSH 的技能没有 `disabled` 字段(MCP 有,技能没有)。**但宿主原生支持的两个调用面字段,组合起来就是「停用」**——这也是 VS Code / Copilot 官方文档里叫 **“Disabled skills”** 的那一档:
55
55
 
56
- - 停用后技能从宿主目录里**消失**(宿主按固定路径读 `SKILL.md`,读不到就跳过),
57
- 所以已停用的技能由本面板**自己扫描** `.disabled` 得出,否则你根本看不到它、也没法再启用;
56
+ | 你想要的效果 | `disable-model-invocation` | `user-invocable` |
57
+ |---|---|---|
58
+ | 都能用(缺省) | 不写 | 不写 |
59
+ | **仅你手动可调用**(模型不自动用) | `true` | 不写 |
60
+ | **停用**(模型不自动用,你也不能手动调用) | `true` | `false` |
61
+
62
+ 所以面板的「停用」写的就是这两个字段:模型拿不到它、你也不能用 `/名称` 调用它,
63
+ 而技能**仍在磁盘原处**、宿主照样报告它、面板照样列出它。
64
+
65
+ - **不搬文件**。技能文件不会被改名、移动或删除——你可以继续用编辑器打开它,
66
+ 其他也读 `~/.agents/skills` 的工具(VS Code / Copilot、Codex 等)也不会因为这里的开关而
67
+ 认为你的技能坏了。⚠️ 1.6.x 及更早的面板用的是「改名成 `SKILL.md.disabled`」那套自创做法,
68
+ 它会让你在**别的工具里**的技能显示异常;旧版本留下的这种文件面板仍能列出来并一键恢复,
69
+ 恢复后请用新的停用方式。
70
+ - **两个面在详情页各自可拨**(「模型自动调用」与「用户可调用」各一个开关),「已停用」是
71
+ 两者都为「否」时的**派生显示**——不是第三个状态,所以不会与文件里的内容对不上。
72
+ - ⚠️ 关于「启用」:它把两个面**都**恢复成可调用。若你原本只要「仅你手动可调用」那一档
73
+ (手工设过 `disable-model-invocation: true`),一次「停用→启用」往返会把它重置——
74
+ 但**面板上就能拨回来**(把「模型自动调用」关掉即可),不必去编辑文件。
58
75
  - 内置(bundled)技能只读,不可启停或删除。
59
76
 
60
77
  ## 允许/禁止模型自动调用
@@ -63,20 +80,49 @@ DSH 的技能没有「disabled」字段(MCP 有,技能没有),所以启
63
80
  `disable-model-invocation: true` 表达后者;面板把它做成详情页「技能属性」里的一个开关(**模型自动调用**),
64
81
  不必再去手工翻 `SKILL.md`。列表行上那个「仅用户可调用」徽章说的就是同一件事。
65
82
 
66
- - **状态一律取自技能文件本身**,不取宿主的发现缓存:宿主对同一个文件的改写会继续报旧值(停用改名就是这样,实测过),
83
+ 它的**反面**是 `user-invocable: false`(你也不能手动调用它)。两个都关掉就是「已停用」,
84
+ 见上一节。面板把两者**各自**做成一个开关,因为它们的效果不同、可以独立组合:
85
+
86
+ | 你想要的效果 | 模型自动调用 | 用户可调用 |
87
+ |---|---|---|
88
+ | 都能用(缺省) | 开 | 开 |
89
+ | 仅你手动可调用(模型不自动用) | **关** | 开 |
90
+ | 仅模型可自动调用(你别手动调) | 开 | **关** |
91
+ | **停用** | **关** | **关** |
92
+
93
+ - **状态一律取自技能文件本身**,不取宿主的发现缓存:宿主对同一个文件的改写会继续报旧值(实测过),
67
94
  而这里正是面板要**写**的东西——写盘后显示回退,用户看到的是「点了没反应」。所以行与详情里的调用权限都是
68
95
  从那个文件里解析出来的,与正文同一份字节。
69
96
  - **读不出来就说「不知道」**:frontmatter 非法的技能宿主会整条跳过,此时面板显示「调用权限未知」且开关不可点。
70
- 编一个「不可调用」出来就是撒谎——**已停用**的技能此前正是这样被显示的(它的权限只有那份 `.disabled` 文件能回答),
71
- 这个缺陷随本开关一并修掉。
97
+ 编一个「不可调用」出来就是撒谎。
98
+ - **定位按磁盘上的真实位置,不按技能名**:宿主允许技能文件/目录的名字与 frontmatter 里的 `name` 不一致
99
+ (技能名取自 frontmatter),面板因此按「宿主报告过的根 + 那个根下的条目」定位。这类技能以前在面板里
100
+ 所有操作都会失败(读正文、启停、删除全报「找不到技能」),现在与其他技能一视同仁。
72
101
  - **只改那一行,不重排你的文件**:关闭时把那**一行**删掉(缺省就是「允许」,所以关掉不留痕迹,开→关能逐字节还原原文);
73
102
  打开时若已有该字段就地把值改成 `true`,没有才在 frontmatter **末尾**追加一行。字段顺序、缩进、正文、CRLF 风格都不动。
74
103
  - **形状读不准就拒绝**:值是块标量或跨了多行、要在带行尾注释的那一行上**打开**它(关掉是整行删除,
75
104
  边界确定,注释随行而去)、或文件里有宿主会拒绝的驼峰键(`disableModelInvocation`)时,
76
105
  面板会拒绝并在界面上说明原因,让你手工改——猜一个改法等于改坏你的技能文件。
77
- - **生效时机**:写的是文件,宿主**重新发现**这条技能之后才影响模型(重启 DSH 最稳妥)。面板显示的是磁盘上的值。
106
+ - **生效时机**:写的是文件,宿主发现这条技能变了之后才影响模型——**通常不到一秒**。
107
+ 宿主用文件监听盯住技能根,改动会自动生效(实测 100~300ms),面板显示的就是磁盘上的值,
108
+ **不必重启 DSH**。唯一例外:宿主启动时若被配置成不监听(`skill-filesystem` 的 `watch: false`),
109
+ 那才需要重启——真机默认是监听的。详见下一节。
78
110
  - 内置(bundled)技能只读,开关呈**禁用**(与启停/删除同一约定:渲染出来但点不动)。
79
111
 
112
+ ## 改了技能,多久生效
113
+
114
+ **宿主侧:自动、很快。** DSH 用文件监听盯住每个技能根,技能的新增、改名、删除与内容改动
115
+ 都会让它作废自己的技能目录——**实测 100~300ms**,无需重启。你在面板里启停/改权限之后,
116
+ 模型下一次看技能列表时就是新的(本面板写的就是那个文件)。
117
+
118
+ **面板侧:切回标签页时重取。** 面板不能做到"被推送"——宿主有个 `skills/change` 事件,
119
+ 但它**不在**宿主转发的白名单里,而那份白名单由宿主自己独占(只允许注册一个源),
120
+ 第三方插件没有合法途径把它送到浏览器。所以面板采取的是:**动作后重取 + 切回标签页时重取**。
121
+ 对用户的表现是「切回来就是新的」,而不是假装能实时推送。
122
+
123
+ ⚠️ 如果你改的是**技能文件本身**(用编辑器),面板要等你切回标签页才刷新;而模型侧不用等,
124
+ 宿主自己就发现了。
125
+
80
126
  ## 安装技能
81
127
 
82
128
  市场里点「安装」会先给你一份披露(安装确认弹窗):