@pi-harness/pi-harness 0.1.31 → 0.1.33

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.
Files changed (280) hide show
  1. package/apps/web/dist/assets/index-CZMbiZj6.css +2 -0
  2. package/apps/web/dist/assets/index-DLcE8YQ7.js +26 -0
  3. package/apps/web/dist/index.html +2 -2
  4. package/apps/web/package.json +1 -1
  5. package/apps/web/server-dist/bin.js +19 -3
  6. package/apps/web/server-dist/bin.js.map +1 -1
  7. package/apps/web/src/server/bin.ts +18 -2
  8. package/apps/web/src/style.css +51 -1
  9. package/node_modules/@pi-harness/api-gateway/dist/index.d.ts.map +1 -1
  10. package/node_modules/@pi-harness/api-gateway/dist/index.js +117 -11
  11. package/node_modules/@pi-harness/api-gateway/dist/index.js.map +1 -1
  12. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/agent-teams.json +5 -5
  13. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/anchored-standard.json +3 -3
  14. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/annotation.json +3 -3
  15. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/archify.json +3 -3
  16. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/at-file.json +3 -11
  17. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/auto-mode.json +3 -3
  18. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/better-sidebar.json +5 -5
  19. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/browser-fetch.json +3 -10
  20. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/browser-session.json +3 -10
  21. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/canvas-draw.json +4 -4
  22. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/change-verifier.json +3 -3
  23. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cleaner.json +3 -11
  24. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cli-notifier.json +3 -3
  25. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/code2skill.json +5 -5
  26. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/colleague-skill.json +3 -3
  27. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/context-doctor.json +3 -10
  28. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/context.json +3 -9
  29. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cordis-group.json +12 -5
  30. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cordis-logger-console.json +16 -5
  31. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cordis-timer.json +11 -5
  32. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cost-meter.json +3 -3
  33. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/dependency-checker.json +4 -12
  34. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/docker-sandbox.json +3 -11
  35. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/fail-logger.json +3 -10
  36. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/genui.json +3 -3
  37. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/git-time-capsule.json +3 -10
  38. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/graph-memory.json +5 -5
  39. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/history-compressor.json +4 -4
  40. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/hol-guard.json +4 -4
  41. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/i18n-pair.json +3 -9
  42. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/image-compressor.json +4 -4
  43. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/llm-verifier.json +5 -5
  44. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mcp-client.json +5 -11
  45. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mcp-panel.json +5 -5
  46. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/memory.json +5 -5
  47. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mirage-bridge.json +5 -5
  48. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mock-server.json +5 -5
  49. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/modlens.json +3 -3
  50. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/module-search.json +3 -3
  51. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/obsidian-sync.json +3 -3
  52. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/openpets.json +4 -10
  53. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plan-execute.json +3 -3
  54. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-check.json +3 -3
  55. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-dev.json +3 -3
  56. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-finder.json +3 -3
  57. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-radar.json +3 -3
  58. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-stars.json +3 -10
  59. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/prompt-guard.json +4 -4
  60. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/prompt-library.json +3 -3
  61. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/readme-gen.json +3 -11
  62. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/recall-unread.json +3 -10
  63. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/reverse-skill.json +4 -4
  64. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/reviewer-bot.json +3 -3
  65. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/runtime-doctor.json +3 -3
  66. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/secure-audit.json +3 -3
  67. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-bookmarks.json +3 -3
  68. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-bridge.json +3 -11
  69. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-compare.json +3 -3
  70. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-export.json +3 -3
  71. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-insights.json +3 -11
  72. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-search.json +3 -3
  73. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/skill-catalog.json +3 -3
  74. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/skill-guard.json +4 -11
  75. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/sql-lens.json +5 -12
  76. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/synapse.json +3 -3
  77. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/tab-manager.json +3 -3
  78. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/taskboard.json +3 -3
  79. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/telemetry-blocker.json +3 -3
  80. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/test-harness.json +5 -13
  81. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/theme-studio.json +3 -3
  82. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/token-guard.json +3 -11
  83. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/turn-rewind.json +3 -10
  84. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/undo-savepoint.json +3 -3
  85. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/vision-toolkit.json +3 -9
  86. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/web-research.json +3 -3
  87. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/workspace-navigator.json +3 -3
  88. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/workspace-search.json +4 -4
  89. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/yaml-validator.json +3 -9
  90. package/node_modules/@pi-harness/api-gateway/dist/marketplace.d.ts +18 -2
  91. package/node_modules/@pi-harness/api-gateway/dist/marketplace.d.ts.map +1 -1
  92. package/node_modules/@pi-harness/api-gateway/dist/marketplace.js +93 -10
  93. package/node_modules/@pi-harness/api-gateway/dist/marketplace.js.map +1 -1
  94. package/node_modules/@pi-harness/api-gateway/package.json +1 -1
  95. package/node_modules/@pi-harness/cli/dist/main.d.ts.map +1 -1
  96. package/node_modules/@pi-harness/cli/dist/main.js +21 -3
  97. package/node_modules/@pi-harness/cli/dist/main.js.map +1 -1
  98. package/node_modules/@pi-harness/cli/package.json +1 -1
  99. package/node_modules/@pi-harness/host-webserver/package.json +1 -1
  100. package/node_modules/@pi-harness/web-app/package.json +1 -1
  101. package/package.json +6 -6
  102. package/packages/api-gateway/dist/index.d.ts.map +1 -1
  103. package/packages/api-gateway/dist/index.js +117 -11
  104. package/packages/api-gateway/dist/index.js.map +1 -1
  105. package/packages/api-gateway/dist/marketplace-entries/official/agent-teams.json +5 -5
  106. package/packages/api-gateway/dist/marketplace-entries/official/anchored-standard.json +3 -3
  107. package/packages/api-gateway/dist/marketplace-entries/official/annotation.json +3 -3
  108. package/packages/api-gateway/dist/marketplace-entries/official/archify.json +3 -3
  109. package/packages/api-gateway/dist/marketplace-entries/official/at-file.json +3 -11
  110. package/packages/api-gateway/dist/marketplace-entries/official/auto-mode.json +3 -3
  111. package/packages/api-gateway/dist/marketplace-entries/official/better-sidebar.json +5 -5
  112. package/packages/api-gateway/dist/marketplace-entries/official/browser-fetch.json +3 -10
  113. package/packages/api-gateway/dist/marketplace-entries/official/browser-session.json +3 -10
  114. package/packages/api-gateway/dist/marketplace-entries/official/canvas-draw.json +4 -4
  115. package/packages/api-gateway/dist/marketplace-entries/official/change-verifier.json +3 -3
  116. package/packages/api-gateway/dist/marketplace-entries/official/cleaner.json +3 -11
  117. package/packages/api-gateway/dist/marketplace-entries/official/cli-notifier.json +3 -3
  118. package/packages/api-gateway/dist/marketplace-entries/official/code2skill.json +5 -5
  119. package/packages/api-gateway/dist/marketplace-entries/official/colleague-skill.json +3 -3
  120. package/packages/api-gateway/dist/marketplace-entries/official/context-doctor.json +3 -10
  121. package/packages/api-gateway/dist/marketplace-entries/official/context.json +3 -9
  122. package/packages/api-gateway/dist/marketplace-entries/official/cordis-group.json +12 -5
  123. package/packages/api-gateway/dist/marketplace-entries/official/cordis-logger-console.json +16 -5
  124. package/packages/api-gateway/dist/marketplace-entries/official/cordis-timer.json +11 -5
  125. package/packages/api-gateway/dist/marketplace-entries/official/cost-meter.json +3 -3
  126. package/packages/api-gateway/dist/marketplace-entries/official/dependency-checker.json +4 -12
  127. package/packages/api-gateway/dist/marketplace-entries/official/docker-sandbox.json +3 -11
  128. package/packages/api-gateway/dist/marketplace-entries/official/fail-logger.json +3 -10
  129. package/packages/api-gateway/dist/marketplace-entries/official/genui.json +3 -3
  130. package/packages/api-gateway/dist/marketplace-entries/official/git-time-capsule.json +3 -10
  131. package/packages/api-gateway/dist/marketplace-entries/official/graph-memory.json +5 -5
  132. package/packages/api-gateway/dist/marketplace-entries/official/history-compressor.json +4 -4
  133. package/packages/api-gateway/dist/marketplace-entries/official/hol-guard.json +4 -4
  134. package/packages/api-gateway/dist/marketplace-entries/official/i18n-pair.json +3 -9
  135. package/packages/api-gateway/dist/marketplace-entries/official/image-compressor.json +4 -4
  136. package/packages/api-gateway/dist/marketplace-entries/official/llm-verifier.json +5 -5
  137. package/packages/api-gateway/dist/marketplace-entries/official/mcp-client.json +5 -11
  138. package/packages/api-gateway/dist/marketplace-entries/official/mcp-panel.json +5 -5
  139. package/packages/api-gateway/dist/marketplace-entries/official/memory.json +5 -5
  140. package/packages/api-gateway/dist/marketplace-entries/official/mirage-bridge.json +5 -5
  141. package/packages/api-gateway/dist/marketplace-entries/official/mock-server.json +5 -5
  142. package/packages/api-gateway/dist/marketplace-entries/official/modlens.json +3 -3
  143. package/packages/api-gateway/dist/marketplace-entries/official/module-search.json +3 -3
  144. package/packages/api-gateway/dist/marketplace-entries/official/obsidian-sync.json +3 -3
  145. package/packages/api-gateway/dist/marketplace-entries/official/openpets.json +4 -10
  146. package/packages/api-gateway/dist/marketplace-entries/official/plan-execute.json +3 -3
  147. package/packages/api-gateway/dist/marketplace-entries/official/plugin-check.json +3 -3
  148. package/packages/api-gateway/dist/marketplace-entries/official/plugin-dev.json +3 -3
  149. package/packages/api-gateway/dist/marketplace-entries/official/plugin-finder.json +3 -3
  150. package/packages/api-gateway/dist/marketplace-entries/official/plugin-radar.json +3 -3
  151. package/packages/api-gateway/dist/marketplace-entries/official/plugin-stars.json +3 -10
  152. package/packages/api-gateway/dist/marketplace-entries/official/prompt-guard.json +4 -4
  153. package/packages/api-gateway/dist/marketplace-entries/official/prompt-library.json +3 -3
  154. package/packages/api-gateway/dist/marketplace-entries/official/readme-gen.json +3 -11
  155. package/packages/api-gateway/dist/marketplace-entries/official/recall-unread.json +3 -10
  156. package/packages/api-gateway/dist/marketplace-entries/official/reverse-skill.json +4 -4
  157. package/packages/api-gateway/dist/marketplace-entries/official/reviewer-bot.json +3 -3
  158. package/packages/api-gateway/dist/marketplace-entries/official/runtime-doctor.json +3 -3
  159. package/packages/api-gateway/dist/marketplace-entries/official/secure-audit.json +3 -3
  160. package/packages/api-gateway/dist/marketplace-entries/official/session-bookmarks.json +3 -3
  161. package/packages/api-gateway/dist/marketplace-entries/official/session-bridge.json +3 -11
  162. package/packages/api-gateway/dist/marketplace-entries/official/session-compare.json +3 -3
  163. package/packages/api-gateway/dist/marketplace-entries/official/session-export.json +3 -3
  164. package/packages/api-gateway/dist/marketplace-entries/official/session-insights.json +3 -11
  165. package/packages/api-gateway/dist/marketplace-entries/official/session-search.json +3 -3
  166. package/packages/api-gateway/dist/marketplace-entries/official/skill-catalog.json +3 -3
  167. package/packages/api-gateway/dist/marketplace-entries/official/skill-guard.json +4 -11
  168. package/packages/api-gateway/dist/marketplace-entries/official/sql-lens.json +5 -12
  169. package/packages/api-gateway/dist/marketplace-entries/official/synapse.json +3 -3
  170. package/packages/api-gateway/dist/marketplace-entries/official/tab-manager.json +3 -3
  171. package/packages/api-gateway/dist/marketplace-entries/official/taskboard.json +3 -3
  172. package/packages/api-gateway/dist/marketplace-entries/official/telemetry-blocker.json +3 -3
  173. package/packages/api-gateway/dist/marketplace-entries/official/test-harness.json +5 -13
  174. package/packages/api-gateway/dist/marketplace-entries/official/theme-studio.json +3 -3
  175. package/packages/api-gateway/dist/marketplace-entries/official/token-guard.json +3 -11
  176. package/packages/api-gateway/dist/marketplace-entries/official/turn-rewind.json +3 -10
  177. package/packages/api-gateway/dist/marketplace-entries/official/undo-savepoint.json +3 -3
  178. package/packages/api-gateway/dist/marketplace-entries/official/vision-toolkit.json +3 -9
  179. package/packages/api-gateway/dist/marketplace-entries/official/web-research.json +3 -3
  180. package/packages/api-gateway/dist/marketplace-entries/official/workspace-navigator.json +3 -3
  181. package/packages/api-gateway/dist/marketplace-entries/official/workspace-search.json +4 -4
  182. package/packages/api-gateway/dist/marketplace-entries/official/yaml-validator.json +3 -9
  183. package/packages/api-gateway/dist/marketplace.d.ts +18 -2
  184. package/packages/api-gateway/dist/marketplace.d.ts.map +1 -1
  185. package/packages/api-gateway/dist/marketplace.js +93 -10
  186. package/packages/api-gateway/dist/marketplace.js.map +1 -1
  187. package/packages/api-gateway/package.json +1 -1
  188. package/packages/api-gateway/src/index.ts +121 -12
  189. package/packages/api-gateway/src/marketplace-entries/official/agent-teams.json +5 -5
  190. package/packages/api-gateway/src/marketplace-entries/official/anchored-standard.json +3 -3
  191. package/packages/api-gateway/src/marketplace-entries/official/annotation.json +3 -3
  192. package/packages/api-gateway/src/marketplace-entries/official/archify.json +3 -3
  193. package/packages/api-gateway/src/marketplace-entries/official/at-file.json +3 -11
  194. package/packages/api-gateway/src/marketplace-entries/official/auto-mode.json +3 -3
  195. package/packages/api-gateway/src/marketplace-entries/official/better-sidebar.json +5 -5
  196. package/packages/api-gateway/src/marketplace-entries/official/browser-fetch.json +3 -10
  197. package/packages/api-gateway/src/marketplace-entries/official/browser-session.json +3 -10
  198. package/packages/api-gateway/src/marketplace-entries/official/canvas-draw.json +4 -4
  199. package/packages/api-gateway/src/marketplace-entries/official/change-verifier.json +3 -3
  200. package/packages/api-gateway/src/marketplace-entries/official/cleaner.json +3 -11
  201. package/packages/api-gateway/src/marketplace-entries/official/cli-notifier.json +3 -3
  202. package/packages/api-gateway/src/marketplace-entries/official/code2skill.json +5 -5
  203. package/packages/api-gateway/src/marketplace-entries/official/colleague-skill.json +3 -3
  204. package/packages/api-gateway/src/marketplace-entries/official/context-doctor.json +3 -10
  205. package/packages/api-gateway/src/marketplace-entries/official/context.json +3 -9
  206. package/packages/api-gateway/src/marketplace-entries/official/cordis-group.json +12 -5
  207. package/packages/api-gateway/src/marketplace-entries/official/cordis-logger-console.json +16 -5
  208. package/packages/api-gateway/src/marketplace-entries/official/cordis-timer.json +11 -5
  209. package/packages/api-gateway/src/marketplace-entries/official/cost-meter.json +3 -3
  210. package/packages/api-gateway/src/marketplace-entries/official/dependency-checker.json +4 -12
  211. package/packages/api-gateway/src/marketplace-entries/official/docker-sandbox.json +3 -11
  212. package/packages/api-gateway/src/marketplace-entries/official/fail-logger.json +3 -10
  213. package/packages/api-gateway/src/marketplace-entries/official/genui.json +3 -3
  214. package/packages/api-gateway/src/marketplace-entries/official/git-time-capsule.json +3 -10
  215. package/packages/api-gateway/src/marketplace-entries/official/graph-memory.json +5 -5
  216. package/packages/api-gateway/src/marketplace-entries/official/history-compressor.json +4 -4
  217. package/packages/api-gateway/src/marketplace-entries/official/hol-guard.json +4 -4
  218. package/packages/api-gateway/src/marketplace-entries/official/i18n-pair.json +3 -9
  219. package/packages/api-gateway/src/marketplace-entries/official/image-compressor.json +4 -4
  220. package/packages/api-gateway/src/marketplace-entries/official/llm-verifier.json +5 -5
  221. package/packages/api-gateway/src/marketplace-entries/official/mcp-client.json +5 -11
  222. package/packages/api-gateway/src/marketplace-entries/official/mcp-panel.json +5 -5
  223. package/packages/api-gateway/src/marketplace-entries/official/memory.json +5 -5
  224. package/packages/api-gateway/src/marketplace-entries/official/mirage-bridge.json +5 -5
  225. package/packages/api-gateway/src/marketplace-entries/official/mock-server.json +5 -5
  226. package/packages/api-gateway/src/marketplace-entries/official/modlens.json +3 -3
  227. package/packages/api-gateway/src/marketplace-entries/official/module-search.json +3 -3
  228. package/packages/api-gateway/src/marketplace-entries/official/obsidian-sync.json +3 -3
  229. package/packages/api-gateway/src/marketplace-entries/official/openpets.json +4 -10
  230. package/packages/api-gateway/src/marketplace-entries/official/plan-execute.json +3 -3
  231. package/packages/api-gateway/src/marketplace-entries/official/plugin-check.json +3 -3
  232. package/packages/api-gateway/src/marketplace-entries/official/plugin-dev.json +3 -3
  233. package/packages/api-gateway/src/marketplace-entries/official/plugin-finder.json +3 -3
  234. package/packages/api-gateway/src/marketplace-entries/official/plugin-radar.json +3 -3
  235. package/packages/api-gateway/src/marketplace-entries/official/plugin-stars.json +3 -10
  236. package/packages/api-gateway/src/marketplace-entries/official/prompt-guard.json +4 -4
  237. package/packages/api-gateway/src/marketplace-entries/official/prompt-library.json +3 -3
  238. package/packages/api-gateway/src/marketplace-entries/official/readme-gen.json +3 -11
  239. package/packages/api-gateway/src/marketplace-entries/official/recall-unread.json +3 -10
  240. package/packages/api-gateway/src/marketplace-entries/official/reverse-skill.json +4 -4
  241. package/packages/api-gateway/src/marketplace-entries/official/reviewer-bot.json +3 -3
  242. package/packages/api-gateway/src/marketplace-entries/official/runtime-doctor.json +3 -3
  243. package/packages/api-gateway/src/marketplace-entries/official/secure-audit.json +3 -3
  244. package/packages/api-gateway/src/marketplace-entries/official/session-bookmarks.json +3 -3
  245. package/packages/api-gateway/src/marketplace-entries/official/session-bridge.json +3 -11
  246. package/packages/api-gateway/src/marketplace-entries/official/session-compare.json +3 -3
  247. package/packages/api-gateway/src/marketplace-entries/official/session-export.json +3 -3
  248. package/packages/api-gateway/src/marketplace-entries/official/session-insights.json +3 -11
  249. package/packages/api-gateway/src/marketplace-entries/official/session-search.json +3 -3
  250. package/packages/api-gateway/src/marketplace-entries/official/skill-catalog.json +3 -3
  251. package/packages/api-gateway/src/marketplace-entries/official/skill-guard.json +4 -11
  252. package/packages/api-gateway/src/marketplace-entries/official/sql-lens.json +5 -12
  253. package/packages/api-gateway/src/marketplace-entries/official/synapse.json +3 -3
  254. package/packages/api-gateway/src/marketplace-entries/official/tab-manager.json +3 -3
  255. package/packages/api-gateway/src/marketplace-entries/official/taskboard.json +3 -3
  256. package/packages/api-gateway/src/marketplace-entries/official/telemetry-blocker.json +3 -3
  257. package/packages/api-gateway/src/marketplace-entries/official/test-harness.json +5 -13
  258. package/packages/api-gateway/src/marketplace-entries/official/theme-studio.json +3 -3
  259. package/packages/api-gateway/src/marketplace-entries/official/token-guard.json +3 -11
  260. package/packages/api-gateway/src/marketplace-entries/official/turn-rewind.json +3 -10
  261. package/packages/api-gateway/src/marketplace-entries/official/undo-savepoint.json +3 -3
  262. package/packages/api-gateway/src/marketplace-entries/official/vision-toolkit.json +3 -9
  263. package/packages/api-gateway/src/marketplace-entries/official/web-research.json +3 -3
  264. package/packages/api-gateway/src/marketplace-entries/official/workspace-navigator.json +3 -3
  265. package/packages/api-gateway/src/marketplace-entries/official/workspace-search.json +4 -4
  266. package/packages/api-gateway/src/marketplace-entries/official/yaml-validator.json +3 -9
  267. package/packages/api-gateway/src/marketplace.ts +113 -12
  268. package/packages/api-gateway/test/index.test.ts +148 -6
  269. package/packages/api-gateway/test/marketplace-schema.test.ts +12 -0
  270. package/packages/api-gateway/test/marketplace.test.ts +255 -354
  271. package/packages/bundle-web-app/package.json +1 -1
  272. package/packages/cli/dist/main.d.ts.map +1 -1
  273. package/packages/cli/dist/main.js +21 -3
  274. package/packages/cli/dist/main.js.map +1 -1
  275. package/packages/cli/package.json +1 -1
  276. package/packages/cli/src/main.ts +20 -3
  277. package/packages/cli/test/cli.test.ts +20 -0
  278. package/packages/host-webserver/package.json +1 -1
  279. package/apps/web/dist/assets/index-D9ZcvGXi.js +0 -25
  280. package/apps/web/dist/assets/index-DJsNIiIo.css +0 -2
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test, vi } from "vitest";
2
2
  import {
3
3
  MARKETPLACE_CAPABILITIES,
4
4
  MARKETPLACE_CATEGORIES,
@@ -35,157 +35,71 @@ describe("plugin marketplace registry", () => {
35
35
 
36
36
  test("publishes the high-value official plugins in the same marketplace registry", () => {
37
37
  const official = new Map(MARKETPLACE_PLUGINS.filter((plugin) => plugin.source === "official").map((plugin) => [plugin.id, plugin]));
38
- expect(official.get("agent-teams")?.category.id).toBe("collaboration");
38
+ expect(official.get("agent-teams")?.category.id).toBe("workflow");
39
39
  expect(official.get("plugin-stars")?.category.id).toBe("discovery");
40
- expect(official.get("plugin-stars")?.description).toMatch(
41
- /curated.*raw\.githubusercontent\.com.*descriptor-safe.*bounded.*cancellable.*fail-closed.*panel/iu,
42
- );
43
- expect(official.get("plugin-stars")?.capabilities).toEqual([
44
- "curated GitHub ranking search",
45
- "strict bounded snapshot validation",
46
- "redirect-free source fetch",
47
- "cancellable request lifecycle",
48
- "bounded panel inventory",
49
- "validated fail-closed panel reporting",
50
- ]);
51
- expect(official.get("plugin-stars")?.hooks).toEqual(["ranking search tool", "raw GitHub snapshot", "plugin UI"]);
40
+ expect(official.get("plugin-stars")?.description).toMatch(/raw\.githubusercontent\.com.*输入受限.*校验严格.*可取消.*失败即关闭/u);
41
+ expect(official.get("plugin-stars")?.capabilities).toEqual(["network-access"]);
42
+ expect(official.get("plugin-stars")?.hooks).toEqual(["榜单搜索工具", "GitHub 原始快照", "插件面板"]);
52
43
  expect(official.get("plugin-stars")?.profile.config).toEqual({
53
44
  sourceUrl: "https://raw.githubusercontent.com/ywsldxk/dsh-plugin-stars/main/data/plugins.json",
54
45
  limit: 10,
55
46
  timeoutMs: 15_000,
56
47
  });
57
48
  expect(official.get("vision-toolkit")?.category.id).toBe("multimodal");
58
- expect(official.get("vision-toolkit")?.description).toMatch(/signature-verified.*PNG.*JPEG.*GIF.*WebP.*bounded header.*cancellable.*local/iu);
59
- expect(official.get("vision-toolkit")?.capabilities).toEqual([
60
- "bounded workspace image catalog",
61
- "signature-verified image metadata",
62
- "PNG JPEG GIF and WebP dimensions",
63
- "cancellable bounded traversal",
64
- "bounded normalized panel",
65
- ]);
66
- expect(official.get("vision-toolkit")?.hooks).toEqual(["image metadata tools", "workspace access", "plugin UI"]);
49
+ expect(official.get("vision-toolkit")?.description).toMatch(/PNG.*JPEG.*GIF.*WebP.*只读文件头.*可取消.*本机/u);
50
+ expect(official.get("vision-toolkit")?.capabilities).toEqual(["read-only", "reads-files"]);
51
+ expect(official.get("vision-toolkit")?.hooks).toEqual(["图像元数据工具", "工作区访问", "插件面板"]);
67
52
  expect(official.get("vision-toolkit")?.profile.config).toEqual({});
68
53
  expect(official.get("session-bridge")?.category.id).toBe("workflow");
69
- expect(official.get("session-bridge")?.description).toMatch(/preview.*export.*confirmed import.*strictly validated.*bounded.*LLM context.*duplicate/iu);
70
- expect(official.get("session-bridge")?.capabilities).toEqual([
71
- "strict bounded handoff format",
72
- "five-part review preview",
73
- "confirmed context import",
74
- "active-session targeting",
75
- "duplicate import protection",
76
- "bounded operation status",
77
- "bounded normalized panel",
78
- ]);
79
- expect(official.get("session-bridge")?.hooks).toEqual(["session bridge tools", "active session manager", "custom context messages", "plugin UI"]);
54
+ expect(official.get("session-bridge")?.description).toMatch(/预览.*导出.*导入需要确认.*严格校验.*防重复注入/u);
55
+ expect(official.get("session-bridge")?.capabilities).toEqual(["read-only", "session-data"]);
56
+ expect(official.get("session-bridge")?.hooks).toEqual(["会话桥接工具", "当前会话管理器", "自定义上下文消息", "插件面板"]);
80
57
  expect(official.get("session-bridge")?.profile.config).toEqual({});
81
58
  expect(official.get("skill-guard")?.category.id).toBe("security");
82
- expect(official.get("skill-guard")?.description).toMatch(/loaded Skill entry.*bounded heuristic.*does not disable/iu);
83
- expect(official.get("skill-guard")?.capabilities).toEqual([
84
- "bounded loaded-entry audit",
85
- "instruction-override heuristics",
86
- "secret-exfiltration heuristics",
87
- "destructive-command and obfuscation heuristics",
88
- "descriptor-safe cancellable scans",
89
- "bounded normalized panel",
90
- ]);
91
- expect(official.get("skill-guard")?.hooks).toEqual(["skill scan tool", "resource loader", "plugin UI"]);
59
+ expect(official.get("skill-guard")?.description).toMatch(/启发式.*已加载 Skill.*不会停用/u);
60
+ expect(official.get("skill-guard")?.capabilities).toEqual(["read-only", "reads-files"]);
61
+ expect(official.get("skill-guard")?.hooks).toEqual(["skill 扫描工具", "资源加载器", "插件面板"]);
92
62
  expect(official.get("skill-guard")?.profile.config).toEqual({});
93
63
  expect(official.get("cost-meter")?.category.id).toBe("observability");
94
- expect(official.get("cost-meter")?.description).toMatch(/runtime-reported.*UTC daily increments.*bounded local ledger/iu);
95
- expect(official.get("cost-meter")?.capabilities).toEqual([
96
- "UTC daily cost ledger",
97
- "session cost snapshots",
98
- "daily budget monitoring",
99
- "bounded local persistence",
100
- ]);
101
- expect(official.get("cost-meter")?.hooks).toEqual(["agent end", "cost report tool", "plugin UI"]);
64
+ expect(official.get("cost-meter")?.description).toMatch(/运行时上报的费用.*UTC.*本地账本/u);
65
+ expect(official.get("cost-meter")?.capabilities).toEqual(["session-data", "reads-files", "writes-files"]);
66
+ expect(official.get("cost-meter")?.hooks).toEqual(["运行结束", "费用报告工具", "插件面板"]);
102
67
  expect(official.get("cost-meter")?.profile.config).toEqual({ dailyBudget: 0, maxEntries: 365 });
103
68
  expect(official.get("skill-catalog")?.category.id).toBe("discovery");
104
69
  expect(official.get("prompt-guard")?.category.id).toBe("security");
105
70
  expect(official.get("browser-fetch")?.category.id).toBe("web");
106
- expect(official.get("browser-fetch")?.description).toMatch(/bounded.*text.*descriptor-safe.*DNS.*pinn.*redirect.*private.*timeout.*validated.*panel/iu);
107
- expect(official.get("browser-fetch")?.capabilities).toEqual([
108
- "bounded textual HTTP fetch",
109
- "DNS rebinding protection",
110
- "per-hop SSRF validation",
111
- "cancellable request lifecycle",
112
- "bounded browser preview",
113
- "validated fail-closed panel reporting",
114
- ]);
71
+ expect(official.get("browser-fetch")?.description).toMatch(/有上限的文本.*固定 DNS.*内网.*文本类型.*可取消.*超时/u);
72
+ expect(official.get("browser-fetch")?.capabilities).toEqual(["network-access"]);
115
73
  expect(official.get("browser-fetch")?.profile.config).toEqual({ allowPrivate: false, timeoutMs: 20_000 });
116
74
  expect(official.get("web-research")?.category.id).toBe("web");
117
- expect(official.get("mcp-client")?.category.id).toBe("tools");
118
- expect(official.get("mcp-client")?.description).toMatch(/MCP stdio.*bounded.*pagination.*tool.*resource.*prompt.*cancellable.*lifecycle/iu);
119
- expect(official.get("mcp-client")?.capabilities).toEqual([
120
- "bounded MCP stdio transport",
121
- "bounded inventory pagination",
122
- "tool resource and prompt bridging",
123
- "persistent server lifecycle",
124
- "cancellable queued requests",
125
- ]);
126
- expect(official.get("mcp-client")?.hooks).toEqual(["MCP stdio tools", "managed server lifecycle", "plugin UI"]);
75
+ expect(official.get("mcp-client")?.category.id).toBe("developer");
76
+ expect(official.get("mcp-client")?.description).toMatch(/MCP stdio.*工具、资源与提示词.*可取消.*生命周期/u);
77
+ expect(official.get("mcp-client")?.capabilities).toEqual(["runs-commands"]);
78
+ expect(official.get("mcp-client")?.hooks).toEqual(["MCP stdio 工具", "托管服务进程", "插件面板"]);
127
79
  expect(official.get("mcp-client")?.profile.config).toEqual({ servers: [] });
128
80
  expect(official.get("at-file")?.category.id).toBe("context");
129
- expect(official.get("at-file")?.description).toMatch(/bounded 256 KiB.*UTF-8.*canonical.*untrusted.*closing-tag.*cancellable.*sequential/iu);
130
- expect(official.get("at-file")?.capabilities).toEqual([
131
- "256 KiB strict UTF-8 text attachments",
132
- "canonical workspace path confinement",
133
- "no-follow regular-file reads",
134
- "untrusted context framing",
135
- "closing-tag neutralization",
136
- "cancellable sequential execution",
137
- "descriptor-safe normalized panel",
138
- ]);
139
- expect(official.get("at-file")?.hooks).toEqual(["file context tool", "workspace file read", "plugin UI"]);
81
+ expect(official.get("at-file")?.description).toMatch(/256 KiB.*UTF-8.*只读上下文.*工作区内.*符号链接/u);
82
+ expect(official.get("at-file")?.capabilities).toEqual(["read-only", "reads-files"]);
83
+ expect(official.get("at-file")?.hooks).toEqual(["文件上下文工具", "工作区文件读取", "插件面板"]);
140
84
  expect(official.get("at-file")?.profile.config).toEqual({});
141
85
  expect(official.get("dependency-checker")?.category.id).toBe("developer");
142
- expect(official.get("dependency-checker")?.description).toMatch(/bounded.*offline.*read-only.*package\.json.*requirements.*presence.*unresolved/iu);
143
- expect(official.get("dependency-checker")?.capabilities).toEqual([
144
- "2000-declaration local presence scan",
145
- "4096-entry workspace-contained virtualenv scan",
146
- "npm common-intersection conflict detection",
147
- "bounded Python numeric constraint analysis",
148
- "unresolved constraint and directive reporting",
149
- "optional dependency reporting",
150
- "descriptor-safe normalized panel",
151
- ]);
152
- expect(official.get("dependency-checker")?.hooks).toEqual(["dependency check tool", "workspace manifest read", "plugin UI"]);
86
+ expect(official.get("dependency-checker")?.description).toMatch(/离线.*只读.*package\.json.*requirements.*已安装.*约束/u);
87
+ expect(official.get("dependency-checker")?.capabilities).toEqual(["read-only", "reads-files"]);
88
+ expect(official.get("dependency-checker")?.hooks).toEqual(["依赖检查工具", "工作区清单读取", "插件面板"]);
153
89
  expect(official.get("token-guard")?.category.id).toBe("observability");
154
- expect(official.get("token-guard")?.description).toMatch(/descriptor-safe.*one abort.*per run.*context.*billed run-token.*cached.*normalized panel/iu);
155
- expect(official.get("token-guard")?.capabilities).toEqual([
156
- "strict context percentage budget",
157
- "optional billed run-token budget",
158
- "one abort request per run",
159
- "resilient descriptor-safe monitoring",
160
- "bounded streaming inspection cadence",
161
- "bounded abort error status",
162
- "cached normalized panel",
163
- ]);
164
- expect(official.get("token-guard")?.hooks).toEqual(["Pi session lifecycle", "active runtime abort", "session usage and statistics", "plugin UI"]);
90
+ expect(official.get("token-guard")?.description).toMatch(/上下文占比.*计费 token.*一次运行最多发出一次中止.*缓存/u);
91
+ expect(official.get("token-guard")?.capabilities).toEqual(["read-only", "session-data"]);
92
+ expect(official.get("token-guard")?.hooks).toEqual(["Pi 会话生命周期", "当前运行中止", "会话用量与统计", "插件面板"]);
165
93
  expect(official.get("token-guard")?.profile.config).toEqual({ maxPercent: 90, maxRunTokens: 0 });
166
94
  expect(official.get("recall-unread")?.category.id).toBe("workflow");
167
- expect(official.get("recall-unread")?.description).toMatch(/read-only.*bounded.*strict UTF-8.*current workspace.*unanswered.*cached/iu);
168
- expect(official.get("recall-unread")?.capabilities).toEqual([
169
- "bounded current-workspace JSONL discovery",
170
- "strict UTF-8 no-follow session reads",
171
- "unanswered-tail detection",
172
- "cached startup and manual rescans",
173
- "descriptor-safe cancellable queries",
174
- "bounded normalized panel",
175
- ]);
176
- expect(official.get("recall-unread")?.hooks).toEqual(["recall unread tool", "native session storage", "active session manager", "plugin UI"]);
95
+ expect(official.get("recall-unread")?.description).toMatch(/只读.*当前工作区.*还没被回复.*缓存/u);
96
+ expect(official.get("recall-unread")?.capabilities).toEqual(["read-only", "session-data", "reads-files"]);
97
+ expect(official.get("recall-unread")?.hooks).toEqual(["未读回溯工具", "原生会话存储", "当前会话管理器", "插件面板"]);
177
98
  expect(official.get("recall-unread")?.profile.config).toEqual({});
178
99
  expect(official.get("turn-rewind")?.category.id).toBe("workflow");
179
- expect(official.get("turn-rewind")?.description).toMatch(/bounded.*current branch.*queued.*cancellable.*native session tree/iu);
180
- expect(official.get("turn-rewind")?.capabilities).toEqual([
181
- "bounded current-branch candidate scan",
182
- "settled-state queued navigation",
183
- "preserved native session branches",
184
- "optional branch summarization",
185
- "cancellable serialized rewinds",
186
- "bounded normalized operation panel",
187
- ]);
188
- expect(official.get("turn-rewind")?.hooks).toEqual(["session rewind tool", "active session tree", "agent settled lifecycle", "plugin UI"]);
100
+ expect(official.get("turn-rewind")?.description).toMatch(/当前分支.*agent 空闲.*串行回退.*原生会话树/u);
101
+ expect(official.get("turn-rewind")?.capabilities).toEqual(["read-only", "session-data"]);
102
+ expect(official.get("turn-rewind")?.hooks).toEqual(["会话回退工具", "当前会话树", "agent 空闲生命周期", "插件面板"]);
189
103
  expect(official.get("turn-rewind")?.profile.config).toEqual({});
190
104
  expect(official.get("context-doctor")?.category.id).toBe("observability");
191
105
  expect(official.get("history-compressor")?.category.id).toBe("context");
@@ -193,284 +107,134 @@ describe("plugin marketplace registry", () => {
193
107
  expect(official.get("auto-mode")?.category.id).toBe("security");
194
108
  expect(official.get("plan-execute")?.category.id).toBe("workflow");
195
109
  expect(official.get("canvas-draw")?.category.id).toBe("multimodal");
196
- expect(official.get("canvas-draw")?.description).toMatch(/Mermaid flowchart source/iu);
197
- expect(official.get("canvas-draw")?.hooks).toEqual(["plugin UI"]);
110
+ expect(official.get("canvas-draw")?.description).toMatch(/Mermaid 流程图源码/u);
111
+ expect(official.get("canvas-draw")?.hooks).toEqual(["插件面板"]);
198
112
  expect(official.get("image-compressor")?.category.id).toBe("multimodal");
199
- expect(official.get("code2skill")?.category.id).toBe("tools");
200
- expect(official.get("code2skill")?.description).toMatch(/selected workspace source files/iu);
201
- expect(official.get("code2skill")?.capabilities).toEqual(["bounded skill packaging", "source file preservation", "SKILL.md generation"]);
113
+ expect(official.get("code2skill")?.category.id).toBe("developer");
114
+ expect(official.get("code2skill")?.description).toMatch(/选中的工作区源码/u);
115
+ expect(official.get("code2skill")?.capabilities).toEqual(["reads-files", "writes-files"]);
202
116
  expect(official.get("workspace-search")?.category.id).toBe("context");
203
117
  expect(official.get("plugin-check")?.category.id).toBe("security");
204
- expect(official.get("test-harness")?.category.id).toBe("testing");
205
- expect(official.get("test-harness")?.description).toMatch(
206
- /five fixed npm script.*trusted workspace.*12 KiB.*UTF-8.*untrusted.*timeout.*cancellable process-tree/iu,
207
- );
208
- expect(official.get("test-harness")?.capabilities).toEqual([
209
- "five-script verification allowlist",
210
- "real npm exit status and signal reporting",
211
- "12 KiB UTF-8-safe output tail",
212
- "terminal-control sanitization and untrusted framing",
213
- "configurable bounded timeout",
214
- "cancellable process-tree cleanup",
215
- "descriptor-safe normalized panel",
216
- ]);
217
- expect(official.get("test-harness")?.hooks).toEqual(["project verification tool", "workspace npm scripts", "plugin UI"]);
118
+ expect(official.get("test-harness")?.category.id).toBe("developer");
119
+ expect(official.get("test-harness")?.description).toMatch(/五个固定名字的 npm 脚本.*12 KiB.*超时.*取消.*进程树/u);
120
+ expect(official.get("test-harness")?.capabilities).toEqual(["runs-commands"]);
121
+ expect(official.get("test-harness")?.hooks).toEqual(["项目验证工具", "工作区 npm 脚本", "插件面板"]);
218
122
  expect(official.get("test-harness")?.profile.config).toEqual({ timeoutMs: 120_000 });
219
123
  expect(official.get("git-time-capsule")?.category.id).toBe("workflow");
220
- expect(official.get("git-time-capsule")?.description).toMatch(/unstaged tracked Git diff.*byte.*validated.*reverse/iu);
221
- expect(official.get("git-time-capsule")?.capabilities).toEqual([
222
- "byte-exact validated unstaged diff capture",
223
- "exclusive no-clobber capsule publication",
224
- "confirmed deterministic reverse apply",
225
- "bounded 256-capsule inventory",
226
- "cancellable serialized Git operations",
227
- "descriptor-safe normalized panel",
228
- ]);
229
- expect(official.get("git-time-capsule")?.hooks).toEqual(["Git snapshot tool", "Git restore tool", "plugin UI"]);
124
+ expect(official.get("git-time-capsule")?.description).toMatch(/未暂存的 Git 改动.*逐字节.*确认后.*反向应用/u);
125
+ expect(official.get("git-time-capsule")?.capabilities).toEqual(["reads-files", "writes-files", "runs-commands"]);
126
+ expect(official.get("git-time-capsule")?.hooks).toEqual(["Git 快照工具", "Git 还原工具", "插件面板"]);
230
127
  expect(official.get("git-time-capsule")?.profile.config).toEqual({ timeoutMs: 15_000 });
231
- expect(official.get("graph-memory")?.description).toMatch(/bounded typed.*directed relations.*atomic.*cross-session/iu);
232
- expect(official.get("graph-memory")?.capabilities).toEqual([
233
- "bounded typed nodes",
234
- "validated directed relations",
235
- "ranked graph search",
236
- "atomic locked persistence",
237
- ]);
238
- expect(official.get("graph-memory")?.hooks).toEqual(["graph memory tools", "local graph file", "plugin UI"]);
128
+ expect(official.get("graph-memory")?.description).toMatch(/带类型的节点.*有向关系.*加锁.*跨会话/u);
129
+ expect(official.get("graph-memory")?.capabilities).toEqual(["reads-files", "writes-files"]);
130
+ expect(official.get("graph-memory")?.hooks).toEqual(["图记忆工具", "本地图文件", "插件面板"]);
239
131
  expect(official.get("graph-memory")?.profile.config).toEqual({ fileName: "graph-memory.json", maxNodes: 2_000, maxRelations: 5_000 });
240
132
  expect(official.get("yaml-validator")?.category.id).toBe("developer");
241
- expect(official.get("yaml-validator")?.description).toMatch(/workspace-contained.*UTF-8.*multi-document.*bounded.*cancellable/iu);
242
- expect(official.get("yaml-validator")?.capabilities).toEqual([
243
- "workspace-contained bounded reads",
244
- "strict UTF-8 multi-document YAML",
245
- "line-aware error and warning diagnostics",
246
- "cancellable validation lifecycle",
247
- "bounded panel previews",
248
- ]);
249
- expect(official.get("yaml-validator")?.hooks).toEqual(["YAML validation tool", "workspace files", "plugin UI"]);
133
+ expect(official.get("yaml-validator")?.description).toMatch(/工作区内.*UTF-8.*多文档 YAML.*受限.*可取消/u);
134
+ expect(official.get("yaml-validator")?.capabilities).toEqual(["read-only", "reads-files"]);
135
+ expect(official.get("yaml-validator")?.hooks).toEqual(["YAML 校验工具", "工作区文件", "插件面板"]);
250
136
  expect(official.get("yaml-validator")?.profile.config).toEqual({});
251
137
  expect(official.get("browser-session")?.category.id).toBe("web");
252
138
  expect(official.get("browser-session")?.category).toEqual(official.get("browser-fetch")?.category);
253
- expect(official.get("browser-session")?.description).toMatch(/loopback-only.*descriptor-safe.*bounded.*cancellable.*screenshot.*validated.*panel/iu);
254
- expect(official.get("browser-session")?.capabilities).toEqual([
255
- "loopback-only CDP endpoint",
256
- "bounded tab discovery",
257
- "navigation read click and screenshot tools",
258
- "cancellable request lifecycle",
259
- "bounded panel previews",
260
- "validated fail-closed panel reporting",
261
- ]);
262
- expect(official.get("browser-session")?.hooks).toEqual(["browser session tools", "Chrome DevTools Protocol", "plugin UI"]);
139
+ expect(official.get("browser-session")?.description).toMatch(/本机回环.*命令受限.*可取消.*截图元数据/u);
140
+ expect(official.get("browser-session")?.capabilities).toEqual(["network-access"]);
141
+ expect(official.get("browser-session")?.hooks).toEqual(["浏览器会话工具", "Chrome DevTools 协议", "插件面板"]);
263
142
  expect(official.get("browser-session")?.profile.config).toEqual({ endpoint: "http://127.0.0.1:9222" });
264
143
  expect(official.get("docker-sandbox")?.category.id).toBe("security");
265
- expect(official.get("docker-sandbox")?.description).toMatch(
266
- /descriptor-safe.*local-only.*no container network.*read-only workspace.*CPU.*memory.*PID.*sanitized output.*strict panel.*cancellation cleanup/iu,
267
- );
268
- expect(official.get("docker-sandbox")?.capabilities).toEqual([
269
- "local-only image execution",
270
- "no-network containers",
271
- "read-only workspace by default",
272
- "CPU memory and PID limits",
273
- "sanitized bounded output",
274
- "strict panel reporting",
275
- "cancellation cleanup",
276
- ]);
277
- expect(official.get("docker-sandbox")?.hooks).toEqual(["sandbox execution tool", "Docker CLI", "plugin UI"]);
144
+ expect(official.get("docker-sandbox")?.description).toMatch(/本地镜像.*无网络.*只读.*CPU.*内存.*进程数.*输出会清理.*取消/u);
145
+ expect(official.get("docker-sandbox")?.capabilities).toEqual(["reads-files", "writes-files", "runs-commands"]);
146
+ expect(official.get("docker-sandbox")?.hooks).toEqual(["沙箱执行工具", "Docker CLI", "插件面板"]);
278
147
  expect(official.get("docker-sandbox")?.profile.config).toEqual({});
279
- expect(official.get("mock-server")?.category.id).toBe("tools");
280
- expect(official.get("sql-lens")?.category.id).toBe("tools");
281
- expect(official.get("sql-lens")?.description).toMatch(
282
- /workspace.*SQLite.*strict.*descriptor-safe.*single.*read-only.*bounded.*symlink.*timed.*cancellation.*fail-closed.*panel/iu,
283
- );
284
- expect(official.get("sql-lens")?.capabilities).toEqual([
285
- "single-statement SQLite inspection",
286
- "isolated read-only query worker",
287
- "bounded rows cells and result bytes",
288
- "workspace and symlink safety checks",
289
- "timeout and cancellation termination",
290
- "validated fail-closed panel reporting",
291
- ]);
292
- expect(official.get("sql-lens")?.hooks).toEqual(["SQL read-only tool", "workspace SQLite files", "plugin UI"]);
148
+ expect(official.get("mock-server")?.category.id).toBe("developer");
149
+ expect(official.get("sql-lens")?.category.id).toBe("developer");
150
+ expect(official.get("sql-lens")?.description).toMatch(/SQLite.*一次一条只读语句.*有上限.*符号链接.*超时.*取消/u);
151
+ expect(official.get("sql-lens")?.capabilities).toEqual(["reads-files", "runs-commands"]);
152
+ expect(official.get("sql-lens")?.hooks).toEqual(["SQL 只读工具", "工作区 SQLite 文件", "插件面板"]);
293
153
  expect(official.get("sql-lens")?.profile.config).toEqual({ timeoutMs: 5_000 });
294
154
  expect(official.get("i18n-pair")?.category.id).toBe("workflow");
295
- expect(official.get("i18n-pair")?.description).toMatch(
296
- /strict read-only.*bounded JSON locale.*no-follow workspace.*collision-safe missing.*extra.*stable failures.*cancellation.*validated panel/iu,
297
- );
298
- expect(official.get("i18n-pair")?.capabilities).toEqual([
299
- "collision-safe locale key parity",
300
- "strict bounded workspace-only JSON reads",
301
- "no-follow symbolic-link safety",
302
- "stable sequential cancellable inspection",
303
- "validated bounded panel reporting",
304
- ]);
305
- expect(official.get("i18n-pair")?.hooks).toEqual(["i18n check tool", "workspace locale files", "plugin UI"]);
155
+ expect(official.get("i18n-pair")?.description).toMatch(/只读.*JSON 语言文件.*缺失键.*多余键.*符号链接.*可取消/u);
156
+ expect(official.get("i18n-pair")?.capabilities).toEqual(["read-only", "reads-files"]);
157
+ expect(official.get("i18n-pair")?.hooks).toEqual(["i18n 检查工具", "工作区语言文件", "插件面板"]);
306
158
  expect(official.get("i18n-pair")?.profile.config).toEqual({});
307
159
  expect(official.get("plugin-finder")?.category.id).toBe("discovery");
308
160
  expect(official.get("readme-gen")?.category.id).toBe("developer");
309
- expect(official.get("readme-gen")?.description).toMatch(
310
- /strictly bounded manifest.*descriptor-safe loader.*Markdown-safe.*confirmed.*no-clobber atomic.*explicit overwrite.*cancellable/iu,
311
- );
312
- expect(official.get("readme-gen")?.capabilities).toEqual([
313
- "bounded manifest metadata",
314
- "descriptor-safe loader inventory",
315
- "Markdown-safe rendering",
316
- "confirmed no-clobber atomic writes",
317
- "explicit overwrite confirmation",
318
- "cancellable sequential tools",
319
- "bounded normalized operation panel",
320
- ]);
321
- expect(official.get("readme-gen")?.hooks).toEqual([
322
- "README report and write tools",
323
- "workspace manifest and files",
324
- "Cordis loader inventory",
325
- "plugin UI",
326
- ]);
161
+ expect(official.get("readme-gen")?.description).toMatch(/依赖清单.*加载器清单.*Markdown 转义.*不覆盖.*显式确认/u);
162
+ expect(official.get("readme-gen")?.capabilities).toEqual(["reads-files", "writes-files"]);
163
+ expect(official.get("readme-gen")?.hooks).toEqual(["README 报告与写入工具", "工作区清单与文件", "Cordis 加载器清单", "插件面板"]);
327
164
  expect(official.get("readme-gen")?.profile.config).toEqual({});
328
165
  expect(official.get("anchored-standard")?.category.id).toBe("security");
329
- expect(official.get("annotation")?.hooks).toEqual(["plugin UI"]);
166
+ expect(official.get("annotation")?.hooks).toEqual(["插件面板"]);
330
167
  expect(official.get("change-verifier")?.category.id).toBe("workflow");
331
168
  expect(official.get("openpets")?.category.id).toBe("web");
332
- expect(official.get("openpets")?.description).toMatch(
333
- /bounded.*companion state.*validated.*session entr.*without retaining message contents.*persistence/iu,
334
- );
335
- expect(official.get("openpets")?.capabilities).toEqual([
336
- "bounded durable companion state",
337
- "validated session recovery",
338
- "descriptor-safe pet actions",
339
- "session event reactions",
340
- "persistence health panel",
341
- ]);
342
- expect(official.get("openpets")?.hooks).toEqual(["session events", "pet reaction tool", "session storage", "plugin UI"]);
169
+ expect(official.get("openpets")?.description).toMatch(/会话事件.*不保存消息内容.*操作受限.*持久化状态/u);
170
+ expect(official.get("openpets")?.capabilities).toEqual(["read-only", "session-data"]);
171
+ expect(official.get("openpets")?.hooks).toEqual(["会话事件", "宠物反应工具", "会话存储", "插件面板"]);
343
172
  expect(official.get("openpets")?.profile.config).toEqual({});
344
173
  expect(official.get("session-insights")?.category.id).toBe("observability");
345
- expect(official.get("session-insights")?.description).toMatch(
346
- /descriptor-safe.*validated.*cached.*session.*statistics.*confirmed.*settled.*compaction.*model.*usage.*cost.*cancellable/iu,
347
- );
348
- expect(official.get("session-insights")?.capabilities).toEqual([
349
- "validated detached session statistics",
350
- "message token cache cost and context accounting",
351
- "cached active-session panel",
352
- "confirmed settled-session compaction",
353
- "dedicated compaction cancellation",
354
- "single-flight bounded operation status",
355
- "normalized fail-closed browser view",
356
- ]);
357
- expect(official.get("session-insights")?.hooks).toEqual(["session report tool", "active runtime session", "Pi session lifecycle", "plugin UI"]);
174
+ expect(official.get("session-insights")?.description).toMatch(/校验.*缓存.*会话空闲.*确认.*用量.*费用.*可取消/u);
175
+ expect(official.get("session-insights")?.capabilities).toEqual(["read-only", "session-data"]);
176
+ expect(official.get("session-insights")?.hooks).toEqual(["会话报告工具", "当前运行会话", "Pi 会话生命周期", "插件面板"]);
358
177
  expect(official.get("session-insights")?.profile.config).toEqual({});
359
- expect(official.get("mcp-panel")?.category.id).toBe("tools");
178
+ expect(official.get("mcp-panel")?.category.id).toBe("developer");
360
179
  expect(official.get("fail-logger")?.category.id).toBe("observability");
361
- expect(official.get("fail-logger")?.description).toMatch(
362
- /bounded.*extension.*Agent.*non-aborted compaction.*descriptor-safe event inspection.*safe diagnostic.*occurrence counts/iu,
363
- );
364
- expect(official.get("fail-logger")?.capabilities).toEqual([
365
- "bounded failure aggregation",
366
- "descriptor-safe event inspection",
367
- "abort-aware compaction diagnostics",
368
- "occurrence counting",
369
- "safe diagnostic summaries",
370
- "capacity visibility",
371
- ]);
372
- expect(official.get("fail-logger")?.hooks).toEqual(["extension error", "agent end", "compaction end", "plugin UI"]);
373
- expect(official.get("genui")?.description).toMatch(/bounded.*text, badge, and decimal progress.*plain text/iu);
374
- expect(official.get("genui")?.capabilities).toEqual([
375
- "bounded structured cards",
376
- "typed text badge and progress blocks",
377
- "decimal progress validation",
378
- "plain-text HTML handling",
379
- ]);
380
- expect(official.get("genui")?.hooks).toEqual(["GenUI render tool", "plugin UI"]);
180
+ expect(official.get("fail-logger")?.description).toMatch(/插件错误.*agent 失败.*压缩失败.*摘要.*次数.*不留原始载荷/u);
181
+ expect(official.get("fail-logger")?.capabilities).toEqual(["read-only"]);
182
+ expect(official.get("fail-logger")?.hooks).toEqual(["插件错误", "运行结束", "压缩结束", "插件面板"]);
183
+ expect(official.get("genui")?.description).toMatch(/文本、标签和进度条.*纯文本/u);
184
+ expect(official.get("genui")?.capabilities).toEqual(["read-only"]);
185
+ expect(official.get("genui")?.hooks).toEqual(["GenUI 渲染工具", "插件面板"]);
381
186
  expect(official.get("plugin-dev")?.category.id).toBe("developer");
382
- expect(official.get("plugin-dev")?.description).toMatch(/defer.*session resource.*agent.*settled.*single.*reload.*bounded.*trusted.*development/iu);
383
- expect(official.get("plugin-dev")?.capabilities).toEqual([
384
- "settled-session resource reload",
385
- "single-flight reload guard",
386
- "bounded reload status",
387
- "cancellable caller wait",
388
- ]);
389
- expect(official.get("plugin-dev")?.hooks).toEqual(["session resource reload tool", "Pi session lifecycle", "plugin UI"]);
187
+ expect(official.get("plugin-dev")?.description).toMatch(/推迟.*agent 空闲.*只允许一个重载.*本机开发/u);
188
+ expect(official.get("plugin-dev")?.capabilities).toEqual(["read-only", "session-data"]);
189
+ expect(official.get("plugin-dev")?.hooks).toEqual(["会话资源重载工具", "Pi 会话生命周期", "插件面板"]);
390
190
  expect(official.get("plugin-dev")?.profile.config).toEqual({});
391
191
  expect(official.get("session-export")?.category.id).toBe("workflow");
392
192
  expect(official.get("session-search")?.category.id).toBe("discovery");
393
193
  expect(official.get("session-compare")?.category.id).toBe("discovery");
394
194
  expect(official.get("secure-audit")?.category.id).toBe("security");
395
195
  expect(official.get("session-bookmarks")?.category.id).toBe("workflow");
396
- expect(official.get("llm-verifier")?.category.id).toBe("testing");
196
+ expect(official.get("llm-verifier")?.category.id).toBe("developer");
397
197
  expect(official.get("module-search")?.category.id).toBe("discovery");
398
198
  expect(official.get("workspace-navigator")?.category.id).toBe("developer");
399
199
  expect(official.get("reverse-skill")?.category.id).toBe("security");
400
200
  expect(official.get("colleague-skill")?.category.id).toBe("workflow");
401
- expect(official.get("colleague-skill")?.hooks).toEqual(["session storage", "plugin UI"]);
201
+ expect(official.get("colleague-skill")?.hooks).toEqual(["会话存储", "插件面板"]);
402
202
  expect(official.get("context-insights")?.category.id).toBe("context");
403
- expect(official.get("context-insights")?.description).toMatch(
404
- /descriptor-safe.*bounded message composition.*cached.*active-session.*lifecycle.*normalized browser/iu,
405
- );
406
- expect(official.get("context-insights")?.capabilities).toEqual([
407
- "descriptor-safe bounded message composition",
408
- "resilient context usage inspection",
409
- "active-session lifecycle counters",
410
- "cached session-aware panel",
411
- "bounded normalized browser view",
412
- ]);
413
- expect(official.get("context-insights")?.hooks).toEqual(["context inspection tool", "active runtime session", "Pi session lifecycle", "plugin UI"]);
203
+ expect(official.get("context-insights")?.description).toMatch(/上下文占用.*消息构成.*缓存.*会话计数.*上限/u);
204
+ expect(official.get("context-insights")?.capabilities).toEqual(["read-only", "session-data"]);
205
+ expect(official.get("context-insights")?.hooks).toEqual(["上下文检查工具", "当前运行会话", "Pi 会话生命周期", "插件面板"]);
414
206
  expect(official.get("context-insights")?.profile.config).toEqual({});
415
207
  expect(official.get("context-doctor")?.category.id).toBe("observability");
416
- expect(official.get("context-doctor")?.description).toMatch(/descriptor-safe.*bounded.*queued.*agent.*settled.*confirmed.*model.*cost.*cancellable/iu);
417
- expect(official.get("context-doctor")?.capabilities).toEqual([
418
- "descriptor-safe bounded context audit",
419
- "pressure oversized uninspectable and tool-error diagnostics",
420
- "confirmed settled-session compaction",
421
- "dedicated compaction cancellation",
422
- "single-flight bounded operation status",
423
- "cached normalized panel",
424
- ]);
425
- expect(official.get("context-doctor")?.hooks).toEqual([
426
- "context doctor tool",
427
- "session messages and usage",
428
- "Pi session lifecycle",
429
- "manual compaction",
430
- "plugin UI",
431
- ]);
208
+ expect(official.get("context-doctor")?.description).toMatch(/压力来源.*工具报错.*排到 agent 空闲.*确认后.*费用.*可取消/u);
209
+ expect(official.get("context-doctor")?.capabilities).toEqual(["read-only", "session-data"]);
210
+ expect(official.get("context-doctor")?.hooks).toEqual(["上下文体检工具", "会话消息与用量", "Pi 会话生命周期", "手动压缩", "插件面板"]);
432
211
  expect(official.get("context-doctor")?.profile.config).toEqual({});
433
- expect(official.get("cordis-group")?.category.id).toBe("composition");
434
- expect(official.get("cordis-group")?.capabilities).toEqual(["nested plugin composition", "transactional rollback", "lifecycle ownership"]);
435
- expect(official.get("cordis-group")?.hooks).toEqual(["loader entry tree"]);
212
+ expect(official.get("cordis-group")?.category.id).toBe("workflow");
213
+ expect(official.get("cordis-group")?.capabilities).toEqual(["read-only"]);
214
+ expect(official.get("cordis-group")?.hooks).toEqual(["加载器条目树"]);
436
215
  expect(official.get("cordis-logger-console")?.category.id).toBe("observability");
437
- expect(official.get("cordis-logger-console")?.capabilities).toEqual(["bounded console logging", "per-logger severity filtering", "safe recent-log panel"]);
438
- expect(official.get("cordis-logger-console")?.hooks).toEqual(["Cordis logger exporter", "plugin UI"]);
216
+ expect(official.get("cordis-logger-console")?.capabilities).toEqual(["read-only"]);
217
+ expect(official.get("cordis-logger-console")?.hooks).toEqual(["Cordis 日志导出", "插件面板"]);
439
218
  expect(official.get("cordis-logger-console")?.profile.config).toEqual({ levels: { default: 2 }, maxLength: 8_192 });
440
- expect(official.get("cordis-timer")?.category.id).toBe("runtime");
441
- expect(official.get("cordis-timer")?.capabilities).toEqual([
442
- "scheduling",
443
- "lifecycle-owned cancellation",
444
- "async interval iteration",
445
- "throttle and debounce",
446
- ]);
447
- expect(official.get("cordis-timer")?.hooks).toEqual(["Cordis timer service", "plugin UI"]);
219
+ expect(official.get("cordis-timer")?.category.id).toBe("workflow");
220
+ expect(official.get("cordis-timer")?.capabilities).toEqual(["read-only"]);
221
+ expect(official.get("cordis-timer")?.hooks).toEqual(["Cordis 定时服务", "插件面板"]);
448
222
  expect(official.get("prompt-library")?.category.id).toBe("workflow");
449
223
  expect(official.get("cleaner")?.category.id).toBe("developer");
450
- expect(official.get("cleaner")?.description).toMatch(
451
- /strict.*descriptor-safe.*display-safe.*bounded.*Git capsule.*explicit confirmation.*retention.*symlink.*replacement.*cancellable.*fail-closed/iu,
452
- );
453
- expect(official.get("cleaner")?.capabilities).toEqual([
454
- "strict configuration and parameters",
455
- "bounded capsule inventory",
456
- "display-safe capsule filtering",
457
- "confirmed retention cleanup",
458
- "symlink and replacement defenses",
459
- "cancellable serialized cleanup",
460
- "fail-closed normalized panel",
461
- ]);
462
- expect(official.get("cleaner")?.hooks).toEqual(["capsule cleanup tool", "agent capsule directory", "plugin UI"]);
224
+ expect(official.get("cleaner")?.description).toMatch(/Git 胶囊.*保留策略.*显式确认.*符号链接.*文件替换.*可取消/u);
225
+ expect(official.get("cleaner")?.capabilities).toEqual(["reads-files", "writes-files"]);
226
+ expect(official.get("cleaner")?.hooks).toEqual(["胶囊清理工具", "agent 胶囊目录", "插件面板"]);
463
227
  expect(official.get("cleaner")?.profile.config).toEqual({});
464
228
  expect(official.get("cli-notifier")?.category.id).toBe("workflow");
465
- expect(official.get("cli-notifier")?.description).toMatch(/local desktop notifications/iu);
229
+ expect(official.get("cli-notifier")?.description).toMatch(/本机桌面通知/u);
466
230
  expect(official.get("cli-notifier")?.description).not.toMatch(/webhook/iu);
467
- expect(official.get("cli-notifier")?.capabilities).toEqual(["desktop notification", "completion notice", "failure and abort notice"]);
468
- expect(official.get("cli-notifier")?.hooks).toEqual(["agent end", "compaction error", "plugin UI"]);
231
+ expect(official.get("cli-notifier")?.capabilities).toEqual(["runs-commands"]);
232
+ expect(official.get("cli-notifier")?.hooks).toEqual(["运行结束", "压缩失败", "插件面板"]);
469
233
  expect(official.get("obsidian-sync")?.category.id).toBe("workflow");
470
234
  expect(official.get("tab-manager")?.category.id).toBe("workflow");
471
235
  expect(official.get("telemetry-blocker")?.category.id).toBe("security");
472
236
  expect(official.get("runtime-doctor")?.category.id).toBe("developer");
473
- expect(official.get("better-sidebar")?.category.id).toBe("developer");
237
+ expect(official.get("better-sidebar")?.category.id).toBe("interface");
474
238
  });
475
239
 
476
240
  test("lists every plugin as an installable npm package rather than a launcher subpath", () => {
@@ -481,17 +245,19 @@ describe("plugin marketplace registry", () => {
481
245
  });
482
246
 
483
247
  test("filters by query and capability without mutating the registry", () => {
484
- const result = searchMarketplace("timer", "scheduling");
248
+ const result = searchMarketplace("timer", "read-only");
485
249
  expect(result.map((plugin) => plugin.packageName)).toEqual(["@deepseek-ai/cordis-plugin-timer"]);
486
250
  expect(searchMarketplace("does-not-exist")).toEqual([]);
487
251
  expect(MARKETPLACE_PLUGINS.length).toBeGreaterThan(3);
488
252
  });
489
253
 
490
254
  test("filters by a declared category independently from capabilities", () => {
491
- const result = searchMarketplace("", "", "runtime");
255
+ const result = searchMarketplace("timer", "", "workflow");
492
256
  expect(result.map((plugin) => plugin.packageName)).toEqual(["@deepseek-ai/cordis-plugin-timer"]);
493
- expect(result[0]?.category).toEqual({ id: "runtime", label: "运行时" });
494
- expect(MARKETPLACE_CATEGORIES).toEqual(expect.arrayContaining([{ id: "runtime", label: "运行时", count: 1 }]));
257
+ expect(result[0]?.category).toEqual({ id: "workflow", label: "工作流" });
258
+ expect(MARKETPLACE_CATEGORIES).toEqual(expect.arrayContaining([{ id: "workflow", label: "工作流", count: 20 }]));
259
+ // A tab that holds a single plugin is a tab nobody clicks, and the filter is only worth its width if each option narrows the list to something worth reading.
260
+ expect(MARKETPLACE_CATEGORIES.every((category) => category.count > 1)).toBe(true);
495
261
  });
496
262
 
497
263
  test("loads one entry per file and paginates the filtered result", () => {
@@ -503,7 +269,44 @@ describe("plugin marketplace registry", () => {
503
269
  pageSize: 2,
504
270
  hasNext: MARKETPLACE_PLUGINS.length > 4,
505
271
  });
506
- expect(MARKETPLACE_CAPABILITIES).toContain("scheduling");
272
+ });
273
+
274
+ // The filter is the first thing a reader touches before installing anything, so what it offers has to be a fixed set that says what a plugin does to the machine, and every entry has to answer it.
275
+ test("describes every entry with the same closed capability vocabulary", () => {
276
+ const vocabulary = MARKETPLACE_CAPABILITIES.map((capability) => capability.id);
277
+ expect(vocabulary).toEqual(["read-only", "session-data", "reads-files", "writes-files", "runs-commands", "local-server", "network-access", "model-calls"]);
278
+ expect(MARKETPLACE_CAPABILITIES.every((capability) => capability.label !== "" && capability.count > 0)).toBe(true);
279
+ expect(MARKETPLACE_CAPABILITIES.reduce((sum, capability) => sum + capability.count, 0)).toBe(
280
+ MARKETPLACE_PLUGINS.reduce((sum, plugin) => sum + plugin.capabilities.length, 0),
281
+ );
282
+ for (const plugin of MARKETPLACE_PLUGINS) {
283
+ expect(plugin.capabilities.length).toBeGreaterThan(0);
284
+ expect(plugin.capabilities.filter((capability) => vocabulary.includes(capability))).toEqual(plugin.capabilities);
285
+ expect(new Set(plugin.capabilities).size).toBe(plugin.capabilities.length);
286
+ // A plugin that says it changes nothing cannot also say it writes, executes, listens, dials out, or spends money.
287
+ if (plugin.capabilities.includes("read-only"))
288
+ expect(
289
+ plugin.capabilities.some((capability) => ["writes-files", "runs-commands", "local-server", "network-access", "model-calls"].includes(capability)),
290
+ ).toBe(false);
291
+ }
292
+ });
293
+
294
+ // The tabs take their label from whichever entry happens to be read first, so two spellings of one category would make the console's own vocabulary depend on file order.
295
+ test("spells each category the same way in every entry", () => {
296
+ const labels = new Map<string, string>();
297
+ for (const plugin of MARKETPLACE_PLUGINS) {
298
+ expect(labels.get(plugin.category.id) ?? plugin.category.label).toBe(plugin.category.label);
299
+ labels.set(plugin.category.id, plugin.category.label);
300
+ }
301
+ expect(labels.size).toBe(MARKETPLACE_CATEGORIES.length);
302
+ });
303
+
304
+ // The ids are what the filter and the URL carry, and the labels are what the reader sees, so a search for either has to find the same plugin.
305
+ test("finds a plugin by a capability id and by its label", () => {
306
+ const byId = searchMarketplace("local-server");
307
+ expect(byId.map((plugin) => plugin.id)).toEqual(["mock-server"]);
308
+ expect(searchMarketplace("监听本地端口").map((plugin) => plugin.id)).toEqual(["mock-server"]);
309
+ expect(searchMarketplace("", "local-server").map((plugin) => plugin.id)).toEqual(["mock-server"]);
507
310
  });
508
311
 
509
312
  test("maps plugin entry points to their published npm package", () => {
@@ -658,6 +461,104 @@ describe("plugin marketplace registry", () => {
658
461
  expect(requests).toBe(3);
659
462
  });
660
463
 
464
+ test("serves the statistics already cached and warms the missing ones in the background", async () => {
465
+ const requests: string[] = [];
466
+ const fetcher = (url: string): Promise<Response> => {
467
+ requests.push(url);
468
+ if (url.startsWith("https://registry.npmjs.org/-/v1/search"))
469
+ return Promise.resolve(
470
+ Response.json({
471
+ objects: [
472
+ {
473
+ package: { name: new URL(url).searchParams.get("text"), date: "2026-08-30T13:14:00.557Z" },
474
+ score: { detail: { quality: 0.9 } },
475
+ },
476
+ ],
477
+ }),
478
+ );
479
+ return Promise.resolve(Response.json({ downloads: 4_200 }));
480
+ };
481
+ const load = createMarketplaceStatisticsLoader({ fetcher, ttlMs: 60_000 });
482
+ const plugin = MARKETPLACE_PLUGINS.find((item) => item.id === "cordis-timer")!;
483
+
484
+ expect(load.readCached([plugin])).toEqual({ ready: false, statistics: new Map() });
485
+ await vi.waitFor(() => expect(requests).toHaveLength(2));
486
+
487
+ expect(load.readCached([plugin])).toEqual({
488
+ ready: true,
489
+ statistics: new Map([["@deepseek-ai/cordis-plugin-timer", { downloads30d: 4_200, quality: 0.9, updatedAt: "2026-08-30T13:14:00.557Z" }]]),
490
+ });
491
+ expect(requests).toHaveLength(2);
492
+ });
493
+
494
+ test("stays ready once a lookup has expired so the catalogue is not reordered back to its unsorted form mid-session", async () => {
495
+ let timestamp = 1_000;
496
+ const fetcher = (url: string): Promise<Response> => {
497
+ if (url.startsWith("https://registry.npmjs.org/-/v1/search"))
498
+ return Promise.resolve(Response.json({ objects: [{ package: { name: "@deepseek-ai/cordis-plugin-timer" }, score: { detail: { quality: 0.5 } } }] }));
499
+ return Promise.resolve(Response.json({ downloads: 10 }));
500
+ };
501
+ const load = createMarketplaceStatisticsLoader({ fetcher, now: () => timestamp, ttlMs: 100 });
502
+ const plugin = MARKETPLACE_PLUGINS.find((item) => item.id === "cordis-timer")!;
503
+
504
+ await load([plugin]);
505
+ timestamp += 101;
506
+
507
+ // The entry is stale and is refreshed in the background, but it has been looked up, so the caller keeps sorting on statistics instead of dropping the whole grid back to catalogue order for one poll.
508
+ const cached = load.readCached([plugin]);
509
+ expect(cached.ready).toBe(true);
510
+ expect(cached.statistics.size).toBe(0);
511
+ });
512
+
513
+ test("keeps the npm fan-out below the configured concurrency instead of asking for every package at once", async () => {
514
+ let active = 0;
515
+ let peak = 0;
516
+ const fetcher = async (): Promise<Response> => {
517
+ active += 1;
518
+ peak = Math.max(peak, active);
519
+ await new Promise((resolve) => setTimeout(resolve, 0));
520
+ active -= 1;
521
+ return Response.json({ objects: [] });
522
+ };
523
+ const base = MARKETPLACE_PLUGINS.find((plugin) => plugin.id === "cordis-timer")!;
524
+ const plugins = Array.from({ length: 12 }, (_, index) => ({ ...base, id: `bulk-${index}`, packageName: `bulk-package-${index}` }));
525
+ const load = createMarketplaceStatisticsLoader({ concurrency: 3, fetcher });
526
+
527
+ await load(plugins);
528
+
529
+ expect(peak).toBe(3);
530
+ });
531
+
532
+ test("backs off further on every consecutive npm failure so an open console stops re-issuing the same lookups", async () => {
533
+ let timestamp = 1_000;
534
+ let requests = 0;
535
+ const fetcher = (): Promise<Response> => {
536
+ requests += 1;
537
+ throw new Error("npm unavailable");
538
+ };
539
+ const load = createMarketplaceStatisticsLoader({ failureMaxTtlMs: 400, failureTtlMs: 100, fetcher, now: () => timestamp });
540
+ const plugin = MARKETPLACE_PLUGINS.find((item) => item.id === "cordis-timer")!;
541
+
542
+ await load([plugin]);
543
+ timestamp += 101;
544
+ await load([plugin]);
545
+ expect(requests).toBe(2);
546
+
547
+ // The second failure doubles the retry window, so the poll that would have retried under a flat window finds the cache still valid.
548
+ timestamp += 101;
549
+ await load([plugin]);
550
+ expect(requests).toBe(2);
551
+
552
+ timestamp += 100;
553
+ await load([plugin]);
554
+ expect(requests).toBe(3);
555
+
556
+ // The window is capped rather than doubling forever, so the package is retried again once the ceiling elapses.
557
+ timestamp += 401;
558
+ await load([plugin]);
559
+ expect(requests).toBe(4);
560
+ });
561
+
661
562
  test("sorts recommendations before pagination using verification, npm quality, downloads and recency", () => {
662
563
  const base = MARKETPLACE_PLUGINS.find((plugin) => plugin.id === "cordis-timer")!;
663
564
  const plugins = [