@lesliechan721/aw 0.1.0-beta.3

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 (429) hide show
  1. package/dist/authoring/assets/aw-config.d.mts +2 -0
  2. package/dist/authoring/assets/aw-config.mjs +556 -0
  3. package/dist/authoring/assets/aw-config.mjs.map +1 -0
  4. package/dist/authoring/scripts/add-skill.d.mts +2 -0
  5. package/dist/authoring/scripts/add-skill.mjs +355 -0
  6. package/dist/authoring/scripts/add-skill.mjs.map +1 -0
  7. package/dist/authoring/scripts/check-plugin-versions.d.mts +2 -0
  8. package/dist/authoring/scripts/check-plugin-versions.mjs +5 -0
  9. package/dist/authoring/scripts/check-plugin-versions.mjs.map +1 -0
  10. package/dist/authoring/scripts/init-plugin.d.mts +2 -0
  11. package/dist/authoring/scripts/init-plugin.mjs +512 -0
  12. package/dist/authoring/scripts/init-plugin.mjs.map +1 -0
  13. package/dist/authoring/scripts/lib/javascript-import-scanner.d.mts +4 -0
  14. package/dist/authoring/scripts/lib/javascript-import-scanner.mjs +261 -0
  15. package/dist/authoring/scripts/lib/javascript-import-scanner.mjs.map +1 -0
  16. package/dist/authoring/scripts/lib/plugin-root.d.mts +26 -0
  17. package/dist/authoring/scripts/lib/plugin-root.mjs +125 -0
  18. package/dist/authoring/scripts/lib/plugin-root.mjs.map +1 -0
  19. package/dist/authoring/scripts/lib/plugin-version-check.d.mts +55 -0
  20. package/dist/authoring/scripts/lib/plugin-version-check.mjs +777 -0
  21. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -0
  22. package/dist/authoring/scripts/lib/semver.d.mts +9 -0
  23. package/dist/authoring/scripts/lib/semver.mjs +63 -0
  24. package/dist/authoring/scripts/lib/semver.mjs.map +1 -0
  25. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +124 -0
  26. package/dist/authoring/scripts/lib/skill-scaffold.mjs +374 -0
  27. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -0
  28. package/dist/authoring/scripts/plugin-status.d.mts +2 -0
  29. package/dist/authoring/scripts/plugin-status.mjs +96 -0
  30. package/dist/authoring/scripts/plugin-status.mjs.map +1 -0
  31. package/dist/authoring/scripts/validate-plugin.d.mts +2 -0
  32. package/dist/authoring/scripts/validate-plugin.mjs +4442 -0
  33. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -0
  34. package/dist/authoring/scripts/validate-skill.d.mts +2 -0
  35. package/dist/authoring/scripts/validate-skill.mjs +126 -0
  36. package/dist/authoring/scripts/validate-skill.mjs.map +1 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +8 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/scripts/aw-skill-eval-engine.d.ts +13 -0
  41. package/dist/scripts/aw-skill-eval-engine.js +31 -0
  42. package/dist/scripts/aw-skill-eval-engine.js.map +1 -0
  43. package/dist/scripts/bundle-plugins.d.ts +18 -0
  44. package/dist/scripts/bundle-plugins.js +123 -0
  45. package/dist/scripts/bundle-plugins.js.map +1 -0
  46. package/dist/scripts/codex-eval-app-server.d.ts +89 -0
  47. package/dist/scripts/codex-eval-app-server.js +1228 -0
  48. package/dist/scripts/codex-eval-app-server.js.map +1 -0
  49. package/dist/scripts/codex-eval-isolation.d.ts +61 -0
  50. package/dist/scripts/codex-eval-isolation.js +318 -0
  51. package/dist/scripts/codex-eval-isolation.js.map +1 -0
  52. package/dist/scripts/codex-eval-process-management.d.ts +8 -0
  53. package/dist/scripts/codex-eval-process-management.js +69 -0
  54. package/dist/scripts/codex-eval-process-management.js.map +1 -0
  55. package/dist/scripts/codex-eval-process-tree.d.ts +38 -0
  56. package/dist/scripts/codex-eval-process-tree.js +803 -0
  57. package/dist/scripts/codex-eval-process-tree.js.map +1 -0
  58. package/dist/scripts/codex-eval-runtime-config.d.ts +62 -0
  59. package/dist/scripts/codex-eval-runtime-config.js +843 -0
  60. package/dist/scripts/codex-eval-runtime-config.js.map +1 -0
  61. package/dist/scripts/codex-trigger-eval.d.ts +84 -0
  62. package/dist/scripts/codex-trigger-eval.js +968 -0
  63. package/dist/scripts/codex-trigger-eval.js.map +1 -0
  64. package/dist/scripts/eval-plugin-skills-audited.d.ts +6 -0
  65. package/dist/scripts/eval-plugin-skills-audited.js +261 -0
  66. package/dist/scripts/eval-plugin-skills-audited.js.map +1 -0
  67. package/dist/scripts/eval-plugin-skills.d.ts +45 -0
  68. package/dist/scripts/eval-plugin-skills.js +1586 -0
  69. package/dist/scripts/eval-plugin-skills.js.map +1 -0
  70. package/dist/scripts/eval-run-layout.d.ts +17 -0
  71. package/dist/scripts/eval-run-layout.js +26 -0
  72. package/dist/scripts/eval-run-layout.js.map +1 -0
  73. package/dist/scripts/plugin-eval-contract.d.ts +185 -0
  74. package/dist/scripts/plugin-eval-contract.js +695 -0
  75. package/dist/scripts/plugin-eval-contract.js.map +1 -0
  76. package/dist/scripts/plugin-eval-materializer.d.ts +13 -0
  77. package/dist/scripts/plugin-eval-materializer.js +421 -0
  78. package/dist/scripts/plugin-eval-materializer.js.map +1 -0
  79. package/dist/scripts/plugin-staging.d.ts +43 -0
  80. package/dist/scripts/plugin-staging.js +196 -0
  81. package/dist/scripts/plugin-staging.js.map +1 -0
  82. package/dist/scripts/skill-reporting.d.ts +212 -0
  83. package/dist/scripts/skill-reporting.js +922 -0
  84. package/dist/scripts/skill-reporting.js.map +1 -0
  85. package/dist/scripts/test-plugin-scripts.d.ts +7 -0
  86. package/dist/scripts/test-plugin-scripts.js +674 -0
  87. package/dist/scripts/test-plugin-scripts.js.map +1 -0
  88. package/dist/src/adapters/helpers.d.ts +44 -0
  89. package/dist/src/adapters/helpers.js +95 -0
  90. package/dist/src/adapters/helpers.js.map +1 -0
  91. package/dist/src/authoring/workspace-commands.d.ts +46 -0
  92. package/dist/src/authoring/workspace-commands.js +133 -0
  93. package/dist/src/authoring/workspace-commands.js.map +1 -0
  94. package/dist/src/commands/catalog-publish.d.ts +33 -0
  95. package/dist/src/commands/catalog-publish.js +374 -0
  96. package/dist/src/commands/catalog-publish.js.map +1 -0
  97. package/dist/src/commands/catalog-source.d.ts +111 -0
  98. package/dist/src/commands/catalog-source.js +266 -0
  99. package/dist/src/commands/catalog-source.js.map +1 -0
  100. package/dist/src/commands/console.d.ts +3 -0
  101. package/dist/src/commands/console.js +44 -0
  102. package/dist/src/commands/console.js.map +1 -0
  103. package/dist/src/commands/dependency-operation-plan.d.ts +28 -0
  104. package/dist/src/commands/dependency-operation-plan.js +15 -0
  105. package/dist/src/commands/dependency-operation-plan.js.map +1 -0
  106. package/dist/src/commands/init.d.ts +31 -0
  107. package/dist/src/commands/init.js +63 -0
  108. package/dist/src/commands/init.js.map +1 -0
  109. package/dist/src/commands/options.d.ts +68 -0
  110. package/dist/src/commands/options.js +146 -0
  111. package/dist/src/commands/options.js.map +1 -0
  112. package/dist/src/commands/rebuild-command.d.ts +77 -0
  113. package/dist/src/commands/rebuild-command.js +1681 -0
  114. package/dist/src/commands/rebuild-command.js.map +1 -0
  115. package/dist/src/commands/rebuild-integration.d.ts +94 -0
  116. package/dist/src/commands/rebuild-integration.js +3289 -0
  117. package/dist/src/commands/rebuild-integration.js.map +1 -0
  118. package/dist/src/commands/rebuild-operations.d.ts +110 -0
  119. package/dist/src/commands/rebuild-operations.js +791 -0
  120. package/dist/src/commands/rebuild-operations.js.map +1 -0
  121. package/dist/src/commands/review-publication.d.ts +99 -0
  122. package/dist/src/commands/review-publication.js +904 -0
  123. package/dist/src/commands/review-publication.js.map +1 -0
  124. package/dist/src/commands/runtime.d.ts +176 -0
  125. package/dist/src/commands/runtime.js +746 -0
  126. package/dist/src/commands/runtime.js.map +1 -0
  127. package/dist/src/commands/status-v4.d.ts +102 -0
  128. package/dist/src/commands/status-v4.js +844 -0
  129. package/dist/src/commands/status-v4.js.map +1 -0
  130. package/dist/src/commands/status.d.ts +93 -0
  131. package/dist/src/commands/status.js +442 -0
  132. package/dist/src/commands/status.js.map +1 -0
  133. package/dist/src/commands/wizard.d.ts +57 -0
  134. package/dist/src/commands/wizard.js +58 -0
  135. package/dist/src/commands/wizard.js.map +1 -0
  136. package/dist/src/commands/write-commands.d.ts +7 -0
  137. package/dist/src/commands/write-commands.js +167 -0
  138. package/dist/src/commands/write-commands.js.map +1 -0
  139. package/dist/src/constants.d.ts +25 -0
  140. package/dist/src/constants.js +26 -0
  141. package/dist/src/constants.js.map +1 -0
  142. package/dist/src/generators/agents.d.ts +3 -0
  143. package/dist/src/generators/agents.js +46 -0
  144. package/dist/src/generators/agents.js.map +1 -0
  145. package/dist/src/generators/hooks.d.ts +15 -0
  146. package/dist/src/generators/hooks.js +76 -0
  147. package/dist/src/generators/hooks.js.map +1 -0
  148. package/dist/src/generators/instructions.d.ts +13 -0
  149. package/dist/src/generators/instructions.js +66 -0
  150. package/dist/src/generators/instructions.js.map +1 -0
  151. package/dist/src/generators/local-plugin.d.ts +53 -0
  152. package/dist/src/generators/local-plugin.js +1268 -0
  153. package/dist/src/generators/local-plugin.js.map +1 -0
  154. package/dist/src/generators/permissions.d.ts +10 -0
  155. package/dist/src/generators/permissions.js +37 -0
  156. package/dist/src/generators/permissions.js.map +1 -0
  157. package/dist/src/generators/shared.d.ts +2 -0
  158. package/dist/src/generators/shared.js +15 -0
  159. package/dist/src/generators/shared.js.map +1 -0
  160. package/dist/src/index.d.ts +35 -0
  161. package/dist/src/index.js +2344 -0
  162. package/dist/src/index.js.map +1 -0
  163. package/dist/src/output/command-receipt.d.ts +90 -0
  164. package/dist/src/output/command-receipt.js +129 -0
  165. package/dist/src/output/command-receipt.js.map +1 -0
  166. package/dist/src/output/debug-log.d.ts +4 -0
  167. package/dist/src/output/debug-log.js +97 -0
  168. package/dist/src/output/debug-log.js.map +1 -0
  169. package/dist/src/output/equivalent-command.d.ts +9 -0
  170. package/dist/src/output/equivalent-command.js +39 -0
  171. package/dist/src/output/equivalent-command.js.map +1 -0
  172. package/dist/src/output/model.d.ts +117 -0
  173. package/dist/src/output/model.js +145 -0
  174. package/dist/src/output/model.js.map +1 -0
  175. package/dist/src/output/rebuild-progress.d.ts +21 -0
  176. package/dist/src/output/rebuild-progress.js +138 -0
  177. package/dist/src/output/rebuild-progress.js.map +1 -0
  178. package/dist/src/output/redact.d.ts +3 -0
  179. package/dist/src/output/redact.js +39 -0
  180. package/dist/src/output/redact.js.map +1 -0
  181. package/dist/src/output/reporter.d.ts +9 -0
  182. package/dist/src/output/reporter.js +133 -0
  183. package/dist/src/output/reporter.js.map +1 -0
  184. package/dist/src/portable/commands.d.ts +120 -0
  185. package/dist/src/portable/commands.js +506 -0
  186. package/dist/src/portable/commands.js.map +1 -0
  187. package/dist/src/portable/materializer.d.ts +50 -0
  188. package/dist/src/portable/materializer.js +408 -0
  189. package/dist/src/portable/materializer.js.map +1 -0
  190. package/dist/src/portable/skills-cli.d.ts +43 -0
  191. package/dist/src/portable/skills-cli.js +148 -0
  192. package/dist/src/portable/skills-cli.js.map +1 -0
  193. package/dist/src/tui/components.d.ts +32 -0
  194. package/dist/src/tui/components.js +42 -0
  195. package/dist/src/tui/components.js.map +1 -0
  196. package/dist/src/tui/console-app-v4.d.ts +20 -0
  197. package/dist/src/tui/console-app-v4.js +2613 -0
  198. package/dist/src/tui/console-app-v4.js.map +1 -0
  199. package/dist/src/tui/controller-runtime.d.ts +11 -0
  200. package/dist/src/tui/controller-runtime.js +902 -0
  201. package/dist/src/tui/controller-runtime.js.map +1 -0
  202. package/dist/src/tui/controller.d.ts +367 -0
  203. package/dist/src/tui/controller.js +1724 -0
  204. package/dist/src/tui/controller.js.map +1 -0
  205. package/dist/src/tui/index.d.ts +13 -0
  206. package/dist/src/tui/index.js +14 -0
  207. package/dist/src/tui/index.js.map +1 -0
  208. package/dist/src/tui/keys.d.ts +12 -0
  209. package/dist/src/tui/keys.js +22 -0
  210. package/dist/src/tui/keys.js.map +1 -0
  211. package/dist/src/tui/layout.d.ts +12 -0
  212. package/dist/src/tui/layout.js +19 -0
  213. package/dist/src/tui/layout.js.map +1 -0
  214. package/dist/src/tui/model.d.ts +279 -0
  215. package/dist/src/tui/model.js +652 -0
  216. package/dist/src/tui/model.js.map +1 -0
  217. package/dist/src/tui/prompt.d.ts +8 -0
  218. package/dist/src/tui/prompt.js +242 -0
  219. package/dist/src/tui/prompt.js.map +1 -0
  220. package/dist/src/tui/router.d.ts +153 -0
  221. package/dist/src/tui/router.js +102 -0
  222. package/dist/src/tui/router.js.map +1 -0
  223. package/dist/src/tui/runner.d.ts +11 -0
  224. package/dist/src/tui/runner.js +71 -0
  225. package/dist/src/tui/runner.js.map +1 -0
  226. package/dist/src/tui/theme.d.ts +18 -0
  227. package/dist/src/tui/theme.js +20 -0
  228. package/dist/src/tui/theme.js.map +1 -0
  229. package/dist/src/tui/views.d.ts +57 -0
  230. package/dist/src/tui/views.js +173 -0
  231. package/dist/src/tui/views.js.map +1 -0
  232. package/dist/src/tui/wizard.d.ts +11 -0
  233. package/dist/src/tui/wizard.js +321 -0
  234. package/dist/src/tui/wizard.js.map +1 -0
  235. package/dist/src/types.d.ts +313 -0
  236. package/dist/src/types.js +33 -0
  237. package/dist/src/types.js.map +1 -0
  238. package/dist/src/utils/active-marketplace-runtime-lock.d.ts +12 -0
  239. package/dist/src/utils/active-marketplace-runtime-lock.js +133 -0
  240. package/dist/src/utils/active-marketplace-runtime-lock.js.map +1 -0
  241. package/dist/src/utils/agent-version.d.ts +13 -0
  242. package/dist/src/utils/agent-version.js +172 -0
  243. package/dist/src/utils/agent-version.js.map +1 -0
  244. package/dist/src/utils/artifact-plan.d.ts +26 -0
  245. package/dist/src/utils/artifact-plan.js +444 -0
  246. package/dist/src/utils/artifact-plan.js.map +1 -0
  247. package/dist/src/utils/artifact-renderer.d.ts +124 -0
  248. package/dist/src/utils/artifact-renderer.js +442 -0
  249. package/dist/src/utils/artifact-renderer.js.map +1 -0
  250. package/dist/src/utils/artifact-sources.d.ts +8 -0
  251. package/dist/src/utils/artifact-sources.js +10 -0
  252. package/dist/src/utils/artifact-sources.js.map +1 -0
  253. package/dist/src/utils/aw-home-mutation-lock.d.ts +10 -0
  254. package/dist/src/utils/aw-home-mutation-lock.js +55 -0
  255. package/dist/src/utils/aw-home-mutation-lock.js.map +1 -0
  256. package/dist/src/utils/bundle-integrity.d.ts +3 -0
  257. package/dist/src/utils/bundle-integrity.js +88 -0
  258. package/dist/src/utils/bundle-integrity.js.map +1 -0
  259. package/dist/src/utils/bundle-layout.d.ts +4 -0
  260. package/dist/src/utils/bundle-layout.js +546 -0
  261. package/dist/src/utils/bundle-layout.js.map +1 -0
  262. package/dist/src/utils/catalog-digests.d.ts +8 -0
  263. package/dist/src/utils/catalog-digests.js +108 -0
  264. package/dist/src/utils/catalog-digests.js.map +1 -0
  265. package/dist/src/utils/catalog-manifest-v3.d.ts +2 -0
  266. package/dist/src/utils/catalog-manifest-v3.js +3 -0
  267. package/dist/src/utils/catalog-manifest-v3.js.map +1 -0
  268. package/dist/src/utils/catalog-manifest-v4.d.ts +5 -0
  269. package/dist/src/utils/catalog-manifest-v4.js +106 -0
  270. package/dist/src/utils/catalog-manifest-v4.js.map +1 -0
  271. package/dist/src/utils/catalog-public-error.d.ts +15 -0
  272. package/dist/src/utils/catalog-public-error.js +84 -0
  273. package/dist/src/utils/catalog-public-error.js.map +1 -0
  274. package/dist/src/utils/catalog-runtime-projection.d.ts +36 -0
  275. package/dist/src/utils/catalog-runtime-projection.js +172 -0
  276. package/dist/src/utils/catalog-runtime-projection.js.map +1 -0
  277. package/dist/src/utils/catalog-snapshot.d.ts +8 -0
  278. package/dist/src/utils/catalog-snapshot.js +12 -0
  279. package/dist/src/utils/catalog-snapshot.js.map +1 -0
  280. package/dist/src/utils/catalog-source-cache.d.ts +61 -0
  281. package/dist/src/utils/catalog-source-cache.js +276 -0
  282. package/dist/src/utils/catalog-source-cache.js.map +1 -0
  283. package/dist/src/utils/catalog-source-identity.d.ts +4 -0
  284. package/dist/src/utils/catalog-source-identity.js +118 -0
  285. package/dist/src/utils/catalog-source-identity.js.map +1 -0
  286. package/dist/src/utils/catalog-source-presentation.d.ts +1 -0
  287. package/dist/src/utils/catalog-source-presentation.js +32 -0
  288. package/dist/src/utils/catalog-source-presentation.js.map +1 -0
  289. package/dist/src/utils/catalog-source-references.d.ts +65 -0
  290. package/dist/src/utils/catalog-source-references.js +1418 -0
  291. package/dist/src/utils/catalog-source-references.js.map +1 -0
  292. package/dist/src/utils/catalog-sources.d.ts +39 -0
  293. package/dist/src/utils/catalog-sources.js +293 -0
  294. package/dist/src/utils/catalog-sources.js.map +1 -0
  295. package/dist/src/utils/codex-config.d.ts +124 -0
  296. package/dist/src/utils/codex-config.js +1011 -0
  297. package/dist/src/utils/codex-config.js.map +1 -0
  298. package/dist/src/utils/codex-project-surface.d.ts +3 -0
  299. package/dist/src/utils/codex-project-surface.js +49 -0
  300. package/dist/src/utils/codex-project-surface.js.map +1 -0
  301. package/dist/src/utils/codex-roles.d.ts +71 -0
  302. package/dist/src/utils/codex-roles.js +257 -0
  303. package/dist/src/utils/codex-roles.js.map +1 -0
  304. package/dist/src/utils/codex-skill-staging.d.ts +9 -0
  305. package/dist/src/utils/codex-skill-staging.js +8 -0
  306. package/dist/src/utils/codex-skill-staging.js.map +1 -0
  307. package/dist/src/utils/config-value.d.ts +3 -0
  308. package/dist/src/utils/config-value.js +23 -0
  309. package/dist/src/utils/config-value.js.map +1 -0
  310. package/dist/src/utils/dynamic-skill-tree.d.ts +32 -0
  311. package/dist/src/utils/dynamic-skill-tree.js +436 -0
  312. package/dist/src/utils/dynamic-skill-tree.js.map +1 -0
  313. package/dist/src/utils/file-rules.d.ts +1 -0
  314. package/dist/src/utils/file-rules.js +2 -0
  315. package/dist/src/utils/file-rules.js.map +1 -0
  316. package/dist/src/utils/git-catalog-provider.d.ts +7 -0
  317. package/dist/src/utils/git-catalog-provider.js +510 -0
  318. package/dist/src/utils/git-catalog-provider.js.map +1 -0
  319. package/dist/src/utils/hooks-contract.d.ts +1 -0
  320. package/dist/src/utils/hooks-contract.js +2 -0
  321. package/dist/src/utils/hooks-contract.js.map +1 -0
  322. package/dist/src/utils/json.d.ts +4 -0
  323. package/dist/src/utils/json.js +29 -0
  324. package/dist/src/utils/json.js.map +1 -0
  325. package/dist/src/utils/lifecycle-active-store.d.ts +175 -0
  326. package/dist/src/utils/lifecycle-active-store.js +1276 -0
  327. package/dist/src/utils/lifecycle-active-store.js.map +1 -0
  328. package/dist/src/utils/local-marketplace.d.ts +41 -0
  329. package/dist/src/utils/local-marketplace.js +317 -0
  330. package/dist/src/utils/local-marketplace.js.map +1 -0
  331. package/dist/src/utils/manifest.d.ts +14 -0
  332. package/dist/src/utils/manifest.js +36 -0
  333. package/dist/src/utils/manifest.js.map +1 -0
  334. package/dist/src/utils/marketplace.d.ts +3 -0
  335. package/dist/src/utils/marketplace.js +76 -0
  336. package/dist/src/utils/marketplace.js.map +1 -0
  337. package/dist/src/utils/multi-source-catalog.d.ts +72 -0
  338. package/dist/src/utils/multi-source-catalog.js +402 -0
  339. package/dist/src/utils/multi-source-catalog.js.map +1 -0
  340. package/dist/src/utils/native-cli.d.ts +75 -0
  341. package/dist/src/utils/native-cli.js +435 -0
  342. package/dist/src/utils/native-cli.js.map +1 -0
  343. package/dist/src/utils/orphan-marketplace-cleanup.d.ts +18 -0
  344. package/dist/src/utils/orphan-marketplace-cleanup.js +202 -0
  345. package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -0
  346. package/dist/src/utils/ownership.d.ts +130 -0
  347. package/dist/src/utils/ownership.js +766 -0
  348. package/dist/src/utils/ownership.js.map +1 -0
  349. package/dist/src/utils/path-catalog-provider.d.ts +7 -0
  350. package/dist/src/utils/path-catalog-provider.js +87 -0
  351. package/dist/src/utils/path-catalog-provider.js.map +1 -0
  352. package/dist/src/utils/paths.d.ts +41 -0
  353. package/dist/src/utils/paths.js +138 -0
  354. package/dist/src/utils/paths.js.map +1 -0
  355. package/dist/src/utils/platform-entries.d.ts +1 -0
  356. package/dist/src/utils/platform-entries.js +2 -0
  357. package/dist/src/utils/platform-entries.js.map +1 -0
  358. package/dist/src/utils/platform-support.d.ts +1 -0
  359. package/dist/src/utils/platform-support.js +2 -0
  360. package/dist/src/utils/platform-support.js.map +1 -0
  361. package/dist/src/utils/plugin-authoring-capabilities.d.ts +23 -0
  362. package/dist/src/utils/plugin-authoring-capabilities.js +37 -0
  363. package/dist/src/utils/plugin-authoring-capabilities.js.map +1 -0
  364. package/dist/src/utils/plugin-authoring-capabilities.json +92 -0
  365. package/dist/src/utils/plugin-config-write.d.ts +107 -0
  366. package/dist/src/utils/plugin-config-write.js +565 -0
  367. package/dist/src/utils/plugin-config-write.js.map +1 -0
  368. package/dist/src/utils/plugin-config.d.ts +396 -0
  369. package/dist/src/utils/plugin-config.js +1898 -0
  370. package/dist/src/utils/plugin-config.js.map +1 -0
  371. package/dist/src/utils/plugin-dependencies.d.ts +12 -0
  372. package/dist/src/utils/plugin-dependencies.js +162 -0
  373. package/dist/src/utils/plugin-dependencies.js.map +1 -0
  374. package/dist/src/utils/plugin-release.d.ts +2 -0
  375. package/dist/src/utils/plugin-release.js +2 -0
  376. package/dist/src/utils/plugin-release.js.map +1 -0
  377. package/dist/src/utils/plugin-script-contract.d.ts +1 -0
  378. package/dist/src/utils/plugin-script-contract.js +2 -0
  379. package/dist/src/utils/plugin-script-contract.js.map +1 -0
  380. package/dist/src/utils/plugin-scripts.d.ts +172 -0
  381. package/dist/src/utils/plugin-scripts.js +585 -0
  382. package/dist/src/utils/plugin-scripts.js.map +1 -0
  383. package/dist/src/utils/process-lock.d.ts +47 -0
  384. package/dist/src/utils/process-lock.js +532 -0
  385. package/dist/src/utils/process-lock.js.map +1 -0
  386. package/dist/src/utils/process-tree.d.ts +2 -0
  387. package/dist/src/utils/process-tree.js +2 -0
  388. package/dist/src/utils/process-tree.js.map +1 -0
  389. package/dist/src/utils/project-boundary.d.ts +21 -0
  390. package/dist/src/utils/project-boundary.js +95 -0
  391. package/dist/src/utils/project-boundary.js.map +1 -0
  392. package/dist/src/utils/rendered-contract.d.ts +8 -0
  393. package/dist/src/utils/rendered-contract.js +134 -0
  394. package/dist/src/utils/rendered-contract.js.map +1 -0
  395. package/dist/src/utils/runtime-skill-closure.d.ts +13 -0
  396. package/dist/src/utils/runtime-skill-closure.js +863 -0
  397. package/dist/src/utils/runtime-skill-closure.js.map +1 -0
  398. package/dist/src/utils/skill-authoring.d.ts +1 -0
  399. package/dist/src/utils/skill-authoring.js +2 -0
  400. package/dist/src/utils/skill-authoring.js.map +1 -0
  401. package/dist/src/utils/skill-frontmatter-capabilities.d.ts +201 -0
  402. package/dist/src/utils/skill-frontmatter-capabilities.js +5 -0
  403. package/dist/src/utils/skill-frontmatter-capabilities.js.map +1 -0
  404. package/dist/src/utils/skill-frontmatter-capabilities.json +200 -0
  405. package/dist/src/utils/skill-validation.d.ts +37 -0
  406. package/dist/src/utils/skill-validation.js +326 -0
  407. package/dist/src/utils/skill-validation.js.map +1 -0
  408. package/dist/src/utils/slash-commands.d.ts +1 -0
  409. package/dist/src/utils/slash-commands.js +2 -0
  410. package/dist/src/utils/slash-commands.js.map +1 -0
  411. package/dist/src/utils/state-paths.d.ts +13 -0
  412. package/dist/src/utils/state-paths.js +129 -0
  413. package/dist/src/utils/state-paths.js.map +1 -0
  414. package/dist/src/utils/target-eligibility.d.ts +7 -0
  415. package/dist/src/utils/target-eligibility.js +26 -0
  416. package/dist/src/utils/target-eligibility.js.map +1 -0
  417. package/dist/src/utils/target-order.d.ts +7 -0
  418. package/dist/src/utils/target-order.js +10 -0
  419. package/dist/src/utils/target-order.js.map +1 -0
  420. package/dist/src/utils/template-engine.d.ts +1 -0
  421. package/dist/src/utils/template-engine.js +2 -0
  422. package/dist/src/utils/template-engine.js.map +1 -0
  423. package/dist/src/utils/test-path-fence.d.ts +12 -0
  424. package/dist/src/utils/test-path-fence.js +31 -0
  425. package/dist/src/utils/test-path-fence.js.map +1 -0
  426. package/dist/vitest.config.d.ts +2 -0
  427. package/dist/vitest.config.js +16 -0
  428. package/dist/vitest.config.js.map +1 -0
  429. package/package.json +61 -0
@@ -0,0 +1,2613 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Box, Text, useInput, useWindowSize } from 'ink';
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import path from 'node:path';
5
+ import { CATALOG_SOURCE_ID_PATTERN } from '../utils/catalog-sources.js';
6
+ import { canonicalizeCatalogGitUrl } from '../utils/catalog-source-identity.js';
7
+ import { normalizePortableAgents } from '../portable/commands.js';
8
+ import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
9
+ import { ScreenFrame } from './components.js';
10
+ import { extractPortableError, userIssueKey } from './controller.js';
11
+ import { resolveTuiSizePolicy } from './layout.js';
12
+ import { hasInstalledPlacement } from './model.js';
13
+ import { tuiTheme } from './theme.js';
14
+ export const CONSOLE_V4_SCREEN_IDS = [
15
+ 'G01', 'G02', 'G03', 'G04', 'G05',
16
+ 'P01', 'P02', 'P03', 'P04', 'P05', 'P06', 'P07', 'P08', 'P09', 'P10',
17
+ 'P11', 'P12', 'P13', 'P14', 'P15', 'P16', 'P17', 'P18', 'P19', 'P20', 'P21',
18
+ 'C01', 'C02', 'C03', 'C04', 'C05', 'C06', 'C07', 'C08', 'C09', 'C10',
19
+ 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17',
20
+ 'S01', 'S02', 'S03', 'S04', 'S05', 'S06', 'S07', 'S08', 'S09',
21
+ 'S10', 'S11', 'S12', 'S13', 'S14', 'S15', 'S16', 'S17', 'S18',
22
+ 'I01', 'I02', 'I03', 'I04', 'I05', 'I06', 'I07', 'I08', 'I09',
23
+ 'I10', 'I11', 'I12', 'I13',
24
+ 'D01', 'D02', 'D03', 'D04', 'D05', 'D06', 'D07', 'D08', 'D09',
25
+ 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17',
26
+ 'U01', 'U02', 'U03', 'U04', 'U05', 'U06', 'U07', 'U08', 'U09', 'U10', 'U11',
27
+ ];
28
+ function configOwnerKey(route) {
29
+ return `${route.pluginId}\0${route.layer.scope}\0${route.layer.projectLayer}`;
30
+ }
31
+ function configPageMatches(page, route) {
32
+ return Boolean(page && route
33
+ && page.pluginId === route.pluginId
34
+ && page.layer.scope === route.layer.scope
35
+ && page.layer.projectLayer === route.layer.projectLayer);
36
+ }
37
+ function platformLabel(platform) {
38
+ return platform === 'codex' ? 'Codex' : 'Claude Code';
39
+ }
40
+ function partialRetryAction(operationKind, originalAction, targetState) {
41
+ if (!targetState)
42
+ return undefined;
43
+ if (operationKind === 'configSave' || operationKind === 'configReset') {
44
+ return targetState === 'absent' ? undefined : 'reinstall';
45
+ }
46
+ if (originalAction === 'uninstall') {
47
+ return targetState === 'present' || targetState === 'partial' ? 'uninstall' : undefined;
48
+ }
49
+ if (originalAction === 'cleanup')
50
+ return targetState === 'absent' ? undefined : 'cleanup';
51
+ if (targetState === 'absent')
52
+ return 'install';
53
+ if (targetState === 'partial')
54
+ return 'reinstall';
55
+ if (targetState === 'error')
56
+ return undefined;
57
+ return originalAction === 'upgrade' ? 'upgrade' : 'reinstall';
58
+ }
59
+ function scopeLabel(scope) {
60
+ return scope === 'user' ? '所有项目' : '当前项目';
61
+ }
62
+ function statusText(plugin) {
63
+ return {
64
+ available: '未添加',
65
+ installed: '可用',
66
+ needsAttention: '需要处理',
67
+ unavailable: '来源不可用',
68
+ }[plugin.aggregateStatus];
69
+ }
70
+ function statusColor(status) {
71
+ if (status === 'success')
72
+ return tuiTheme.statusColors.success;
73
+ if (status === 'warning')
74
+ return tuiTheme.statusColors.warning;
75
+ if (status === 'error')
76
+ return tuiTheme.statusColors.error;
77
+ if (status === 'info')
78
+ return tuiTheme.statusColors.info;
79
+ if (status === 'muted')
80
+ return tuiTheme.colors.muted;
81
+ return undefined;
82
+ }
83
+ function moveFocus(actions, current, direction) {
84
+ if (actions.length === 0)
85
+ return 0;
86
+ for (let offset = 1; offset <= actions.length; offset += 1) {
87
+ const next = (current + direction * offset + actions.length) % actions.length;
88
+ if (!actions[next]?.disabled)
89
+ return next;
90
+ }
91
+ return current;
92
+ }
93
+ function actionLimitForLineBudget(actions, lineBudget) {
94
+ let used = 0;
95
+ let count = 0;
96
+ for (const action of actions) {
97
+ const height = action.description || action.disabled ? 2 : 1;
98
+ if (count > 0 && used + height > lineBudget)
99
+ break;
100
+ used += height;
101
+ count += 1;
102
+ }
103
+ return Math.max(1, count);
104
+ }
105
+ function lineBudgetViewport(actions, focus, requestedOffset, lineBudget, groupKeys) {
106
+ const height = (start, end) => {
107
+ let lines = 0;
108
+ let previousGroup;
109
+ for (let index = start; index < end; index += 1) {
110
+ const group = groupKeys?.[index];
111
+ if (group && group !== previousGroup)
112
+ lines += 1;
113
+ lines += actions[index]?.description || actions[index]?.disabled ? 2 : 1;
114
+ previousGroup = group;
115
+ }
116
+ return lines;
117
+ };
118
+ let offset = Math.min(requestedOffset, Math.max(0, actions.length - 1));
119
+ if (focus < offset)
120
+ offset = focus;
121
+ while (offset < focus && height(offset, focus + 1) > lineBudget)
122
+ offset += 1;
123
+ let end = Math.max(offset + 1, focus + 1);
124
+ while (end < actions.length && height(offset, end + 1) <= lineBudget)
125
+ end += 1;
126
+ return { offset, limit: Math.max(1, end - offset) };
127
+ }
128
+ function actionViewport(routeName, actions, mode, focus, requestedOffset) {
129
+ if (routeName === 'portableDependencies') {
130
+ return lineBudgetViewport(actions, focus, requestedOffset, mode === 'compact' ? 7 : 12);
131
+ }
132
+ const limit = routeName === 'pluginList' || routeName === 'issueList' || routeName === 'sourceList'
133
+ ? actionLimitForLineBudget(actions, mode === 'compact' ? 8 : 12)
134
+ : routeName === 'pluginDependencies' ? mode === 'compact' ? 2 : 7
135
+ : mode === 'compact' ? 4
136
+ : routeName === 'configFields' || routeName === 'review' || routeName === 'issueDetail' ? 7 : undefined;
137
+ if (limit === undefined)
138
+ return { offset: 0 };
139
+ const maximumOffset = Math.max(0, actions.length - limit);
140
+ let offset = Math.min(requestedOffset, maximumOffset);
141
+ if (focus < offset)
142
+ offset = focus;
143
+ else if (focus >= offset + limit)
144
+ offset = focus - limit + 1;
145
+ return { limit, offset: Math.max(0, offset) };
146
+ }
147
+ function portableSkillCandidateViewport(actions, candidates, mode, focus, requestedOffset) {
148
+ const candidateFocus = focus < candidates.length ? focus : requestedOffset;
149
+ return lineBudgetViewport(actions, candidateFocus, requestedOffset, mode === 'compact' ? 10 : 12, candidates.map((candidate) => candidate.providerPluginId));
150
+ }
151
+ function portableScopeVariantsDiffer(candidate) {
152
+ const project = candidate.scopeDetails.project;
153
+ const user = candidate.scopeDetails.user;
154
+ return Boolean(project && user && (project.description !== user.description
155
+ || project.providerPluginId !== user.providerPluginId
156
+ || project.providerDisplayName !== user.providerDisplayName
157
+ || project.dynamic !== user.dynamic
158
+ || project.filesDigest !== user.filesDigest));
159
+ }
160
+ function portableScopeVariantLabel(scope, detail) {
161
+ if (!detail)
162
+ return '';
163
+ return `${scope === 'project' ? '当前项目' : '所有项目'}:${detail.description} · ${detail.providerDisplayName}${detail.dynamic ? ' · 按设置生成' : ''}`;
164
+ }
165
+ function privatePathInput(value) {
166
+ if (!value)
167
+ return '';
168
+ const basename = value.split(/[\\/]/u).filter(Boolean).at(-1) ?? '已选择';
169
+ return `已选择 / … / ${basename}`;
170
+ }
171
+ function ActionRows(props) {
172
+ const offset = props.offset ?? 0;
173
+ const actions = props.limit === undefined
174
+ ? props.actions
175
+ : props.actions.slice(offset, offset + props.limit);
176
+ return (_jsx(Box, { flexDirection: "column", children: actions.map((action, visibleIndex) => {
177
+ const index = visibleIndex + offset;
178
+ const focused = index === props.focus && !action.disabled;
179
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, backgroundColor: focused ? tuiTheme.colors.selectionBackground : undefined, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { bold: focused, color: action.disabled ? tuiTheme.colors.muted : statusColor(action.status), wrap: "truncate-end", children: [focused ? '› ' : ' ', action.label] }), action.meta ? _jsx(Text, { color: statusColor(action.status) ?? tuiTheme.colors.muted, children: action.meta }) : null] }), action.description || action.disabled
180
+ ? _jsxs(Text, { color: tuiTheme.colors.muted, wrap: "truncate-end", children: [' ', action.disabled ?? action.description] })
181
+ : null] }, `${action.label}:${index}`));
182
+ }) }));
183
+ }
184
+ function PortableSkillRows(props) {
185
+ const end = props.limit === undefined ? props.actions.length : props.offset + props.limit;
186
+ const indexes = props.actions.map((_, index) => index)
187
+ .filter((index) => index >= props.offset && index < end);
188
+ return _jsx(Box, { flexDirection: "column", children: indexes.map((index) => {
189
+ const candidate = props.candidates[index];
190
+ const previous = props.candidates[index - 1];
191
+ const showHeading = candidate && (index === props.offset
192
+ || candidate.providerPluginId !== previous?.providerPluginId);
193
+ const heading = candidate?.relation === 'requested'
194
+ ? '当前插件'
195
+ : candidate ? `来自依赖插件 · ${candidate.providerDisplayName}` : undefined;
196
+ return _jsxs(Box, { flexDirection: "column", children: [showHeading && heading ? _jsx(Text, { bold: true, color: tuiTheme.colors.muted, children: heading }) : null, _jsx(ActionRows, { actions: props.actions, focus: props.focus, offset: index, limit: 1 })] }, index);
197
+ }) });
198
+ }
199
+ function Bordered(props) {
200
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: tuiTheme.colors.border, paddingX: 1, children: [props.title ? _jsx(Text, { bold: true, children: props.title }) : null, props.children] }));
201
+ }
202
+ function SourceTabs(props) {
203
+ const tabs = [{ id: 'all', label: '全部', count: props.controller.listPlugins({ mode: props.mode }).length },
204
+ ...props.controller.listSources().map((source) => ({
205
+ ...source,
206
+ count: props.controller.listPlugins({ mode: props.mode, sourceId: source.id }).length,
207
+ }))];
208
+ const index = Math.max(0, tabs.findIndex((tab) => tab.id === props.current));
209
+ if (props.compact) {
210
+ const tab = tabs[index];
211
+ return _jsxs(Text, { children: ["\u6765\u6E90 ", _jsxs(Text, { color: tuiTheme.colors.accent, children: ["[", tab.label, "]"] }), " ", index + 1, " / ", tabs.length, " \u2190 \u2192"] });
212
+ }
213
+ return (_jsxs(Text, { children: ["\u6765\u6E90 ", tabs.map((tab) => {
214
+ const active = tab.id === props.current;
215
+ const state = 'state' in tab ? tab.state : 'ready';
216
+ return (_jsxs(Text, { bold: active, color: active ? tuiTheme.colors.accent
217
+ : state === 'error' ? tuiTheme.statusColors.error
218
+ : state === 'cache' ? tuiTheme.statusColors.warning : tuiTheme.colors.muted, children: [active ? '[' : '', tab.label, state === 'error' ? ' !' : ` ${tab.count ?? ''}`, active ? ']' : '', ' '] }, tab.id));
219
+ })] }));
220
+ }
221
+ function placementStatus(placement) {
222
+ if (placement.state === 'notInstalled') {
223
+ return { value: '未添加', status: 'muted', description: '选择使用位置后添加' };
224
+ }
225
+ if (placement.state === 'scopeConflict') {
226
+ return { value: '位置冲突', status: 'warning', description: '需要先处理两个安装位置' };
227
+ }
228
+ return {
229
+ value: `${scopeLabel(placement.scope)} · ${placement.health === 'ready' ? '可用' : placement.health === 'incomplete' ? '需要处理' : '不可用'}`,
230
+ status: placement.health === 'ready' ? 'success' : placement.health === 'incomplete' ? 'warning' : 'error',
231
+ description: placement.health === 'ready' ? '当前检查正常' : '查看影响和建议操作',
232
+ };
233
+ }
234
+ function dependencyStatus(item) {
235
+ if (item.aggregateStatus === 'ready')
236
+ return { value: '已可用', status: 'success' };
237
+ if (item.aggregateStatus === 'notInstalled')
238
+ return { value: '未添加', status: 'muted' };
239
+ if (item.aggregateStatus === 'needsAttention')
240
+ return { value: '需要处理', status: 'warning' };
241
+ return { value: '不可用', status: 'error' };
242
+ }
243
+ function dependencyPlacementLabel(item) {
244
+ const placement = item.placements.find((entry) => entry.state === 'installed');
245
+ if (placement?.state === 'installed') {
246
+ const state = placement.health === 'ready' ? '可用'
247
+ : placement.health === 'incomplete' ? '需要处理' : '不可用';
248
+ return `${scopeLabel(placement.scope)} · ${state}`;
249
+ }
250
+ if (item.placements.some((entry) => entry.state === 'scopeConflict'))
251
+ return '位置冲突';
252
+ return dependencyStatus(item).value;
253
+ }
254
+ function dependentTargetLabel(item) {
255
+ const consumer = item.placementReferences?.[0]?.consumer;
256
+ return consumer
257
+ ? `${platformLabel(consumer.platform)} · ${scopeLabel(consumer.scope)}`
258
+ : dependencyPlacementLabel(item);
259
+ }
260
+ function dependencyRelationDescription(item) {
261
+ if (item.relation === 'dependent')
262
+ return '使用这个依赖的插件';
263
+ const requiredBy = item.requiredBy.map((entry) => entry.displayName).join('、');
264
+ return `${item.relation === 'direct' ? '直接依赖' : '间接依赖'}${requiredBy ? ` · ${requiredBy} 需要它` : ''}`;
265
+ }
266
+ function dependencySummaryText(summary) {
267
+ return `${summary.directCount} 个直接 · ${summary.totalCount} 个总计`;
268
+ }
269
+ function operationTargetStateLabel(state) {
270
+ const labels = {
271
+ completed: { label: '已完成', status: 'success' },
272
+ kept: { label: '已保留', status: 'success' },
273
+ failed: { label: '失败', status: 'error' },
274
+ skipped: { label: '未执行 · 依赖未完成', status: 'warning' },
275
+ };
276
+ return labels[state];
277
+ }
278
+ function reviewSymbol(change) {
279
+ return { add: '+', change: '~', remove: '−', keep: '✓', warning: '!' }[change.kind];
280
+ }
281
+ function currentPlugin(controller, route) {
282
+ return 'pluginId' in route ? controller.getPluginOverview(route.pluginId) : undefined;
283
+ }
284
+ export function resolveConsoleV4ScreenId(snapshot, controller, overlay, tooSmall = false, searchMode = false) {
285
+ if (tooSmall)
286
+ return 'G05';
287
+ if (overlay === 'help')
288
+ return 'G03';
289
+ if (overlay === 'discard')
290
+ return 'G04';
291
+ if (snapshot.phase !== 'ready')
292
+ return 'G01';
293
+ const route = snapshot.router.current.route;
294
+ if (route.name === 'home')
295
+ return 'G02';
296
+ if (route.name === 'pluginList') {
297
+ const items = controller.listPlugins({ mode: route.mode, sourceId: route.sourceId });
298
+ if (route.mode === 'installed' && items.length === 0)
299
+ return 'P12';
300
+ if (route.sourceId && controller.getSourceOverview(route.sourceId).source?.state === 'error')
301
+ return 'P03';
302
+ return route.mode === 'installed' ? 'P11' : searchMode ? 'P02' : 'P01';
303
+ }
304
+ if (route.name === 'pluginPreview') {
305
+ return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D01' : 'P04';
306
+ }
307
+ if (route.name === 'portableSkillSelect')
308
+ return 'U01';
309
+ if (route.name === 'portableDependencies')
310
+ return 'U02';
311
+ if (route.name === 'portableAgentMode')
312
+ return 'U03';
313
+ if (route.name === 'portableAgentInput')
314
+ return 'U04';
315
+ if (route.name === 'portableScope')
316
+ return 'U05';
317
+ if (route.name === 'portableBlocker') {
318
+ if (route.code === 'requiredConfigMissing')
319
+ return 'U09';
320
+ if (route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible')
321
+ return 'U10';
322
+ return 'U11';
323
+ }
324
+ if (route.name === 'pluginDependencies') {
325
+ if (route.view === 'removeBlocked')
326
+ return 'D12';
327
+ const plugin = controller.getPluginOverview(route.pluginId);
328
+ return plugin && hasInstalledPlacement(plugin) ? 'D10' : 'D02';
329
+ }
330
+ if (route.name === 'installPlacement') {
331
+ return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D03' : 'P05';
332
+ }
333
+ if (route.name === 'installTools') {
334
+ if (route.view === 'blocked')
335
+ return 'D16';
336
+ return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D04' : 'P06';
337
+ }
338
+ if (route.name === 'pluginDetail') {
339
+ return controller.getPluginOverview(route.pluginId)?.dependencySummary?.totalCount ? 'D09' : 'P13';
340
+ }
341
+ if (route.name === 'platform') {
342
+ const plugin = controller.getPluginOverview(route.pluginId);
343
+ if (plugin?.aggregateStatus === 'unavailable')
344
+ return 'P17';
345
+ const placement = controller.getPlatformPlacement(route.pluginId, route.platform);
346
+ if (!placement || placement.state === 'notInstalled')
347
+ return 'P15';
348
+ if (placement.state === 'installed' && placement.health === 'ready')
349
+ return 'P14';
350
+ return 'P16';
351
+ }
352
+ const dependencyOperation = snapshot.activeReview?.operationKind === 'pluginWrite'
353
+ && Boolean(snapshot.activeReview.changes.some((change) => change.group));
354
+ if (route.name === 'review' && dependencyOperation) {
355
+ return snapshot.activeReview?.pluginAction === 'uninstall' || snapshot.activeReview?.pluginAction === 'cleanup'
356
+ ? 'D11' : 'D05';
357
+ }
358
+ if (route.name === 'progress' && snapshot.progressTargets.length > 0)
359
+ return 'D06';
360
+ if (route.name === 'result' && (snapshot.result?.state === 'succeeded' || snapshot.result?.state === 'partial')
361
+ && snapshot.result.summary.targets?.some((target) => target.relation)) {
362
+ return snapshot.result.state === 'partial' ? 'D08' : 'D07';
363
+ }
364
+ if (route.name === 'review' && snapshot.activeReview?.originIssue)
365
+ return 'I10';
366
+ if (route.name === 'progress' && snapshot.activeReview?.originIssue)
367
+ return 'I11';
368
+ if (route.name === 'result' && snapshot.result?.originIssue) {
369
+ return snapshot.result.state === 'succeeded' ? 'I12' : 'I13';
370
+ }
371
+ if (route.name === 'review' && snapshot.activeReview?.operationKind === 'sourceMutation') {
372
+ return { add: 'S10', update: 'S15', setPrimary: 'S16', remove: 'S18' }[snapshot.activeReview.sourceAction];
373
+ }
374
+ if (route.name === 'progress' && snapshot.activeReview?.operationKind === 'sourceMutation')
375
+ return 'S11';
376
+ if (route.name === 'result' && snapshot.result && 'operationKind' in snapshot.result
377
+ && snapshot.result.operationKind === 'sourceMutation') {
378
+ return snapshot.result.state === 'succeeded' ? 'S12' : 'S13';
379
+ }
380
+ if (route.name === 'review')
381
+ return snapshot.activeReview?.operationKind === 'portableInstall' ? 'U06'
382
+ : snapshot.activeReview?.operationKind === 'configSave'
383
+ ? 'C12'
384
+ : snapshot.activeReview?.operationKind === 'configReset'
385
+ ? 'C16'
386
+ : snapshot.activeReview?.operationKind === 'pluginWrite'
387
+ && (snapshot.activeReview.pluginAction === 'upgrade' || snapshot.activeReview.pluginAction === 'reinstall')
388
+ ? 'P18'
389
+ : snapshot.activeReview?.operationKind === 'pluginWrite'
390
+ && (snapshot.activeReview.pluginAction === 'uninstall' || snapshot.activeReview.pluginAction === 'cleanup')
391
+ ? 'P19' : 'P07';
392
+ if (route.name === 'progress')
393
+ return snapshot.activeReview?.operationKind === 'portableInstall' ? 'U07'
394
+ : snapshot.activeReview?.operationKind === 'configSave'
395
+ || snapshot.activeReview?.operationKind === 'configReset' ? 'C13' : 'P08';
396
+ if (route.name === 'result')
397
+ return snapshot.result?.operationKind === 'portableInstall' ? 'U08'
398
+ : snapshot.result?.operationKind === 'configSave'
399
+ || snapshot.result?.operationKind === 'configReset'
400
+ ? snapshot.result.state === 'succeeded' ? 'C14' : 'C15'
401
+ : snapshot.result?.state === 'partial' ? 'P10' : 'P09';
402
+ if (route.name === 'pluginAbout')
403
+ return 'P20';
404
+ if (route.name === 'pluginTechnical')
405
+ return 'P21';
406
+ if (route.name === 'configLayer')
407
+ return 'C01';
408
+ if (route.name === 'configFields')
409
+ return route.layer.scope === 'effective'
410
+ ? 'C02' : route.groupId ? 'C04' : 'C03';
411
+ if (route.name === 'configFieldEditor')
412
+ return {
413
+ enum: 'C05', boolean: 'C06', number: 'C07', string: 'C08', path: 'C09', sensitive: 'C10',
414
+ }[route.editor];
415
+ if (route.name === 'configPresets')
416
+ return 'C11';
417
+ if (route.name === 'configUnavailable')
418
+ return 'C17';
419
+ if (route.name === 'sourceList')
420
+ return 'S01';
421
+ if (route.name === 'sourceDetail')
422
+ return 'S02';
423
+ if (route.name === 'sourceTechnical')
424
+ return 'S03';
425
+ if (route.name === 'sourceAdd')
426
+ return {
427
+ type: 'S04', id: 'S05', location: 'S06', refPolicy: 'S07', ref: 'S08', subdir: 'S09',
428
+ }[route.step];
429
+ if (route.name === 'sourceEdit')
430
+ return 'S14';
431
+ if (route.name === 'sourceRemoveBlocked')
432
+ return 'S17';
433
+ if (route.name === 'issueList') {
434
+ const issues = controller.listUserIssues();
435
+ return issues.length === 0 ? 'I02' : issues.some((issue) => issue.category.startsWith('dependency')) ? 'D13' : 'I01';
436
+ }
437
+ if (route.name === 'issueDetail') {
438
+ const category = controller.listUserIssues().find((issue) => issue.issueId === route.issueId)?.category;
439
+ return {
440
+ incompleteInstallation: 'I03',
441
+ sourceDegraded: 'I04',
442
+ sourceUnavailable: 'I04',
443
+ sourceMissing: 'I05',
444
+ sourceIdentityChanged: 'I06',
445
+ requiredConfigMissing: 'I07',
446
+ scopeConflict: 'I08',
447
+ dependencyMissing: 'D14',
448
+ dependencyNotReady: 'D15',
449
+ dependencyTargetUnsupported: 'D16',
450
+ dependencyStillRequired: 'D12',
451
+ dependencyGraphInvalid: 'D17',
452
+ legacyState: 'I09',
453
+ diagnostic: 'I09',
454
+ }[category ?? 'diagnostic'];
455
+ }
456
+ return 'G02';
457
+ }
458
+ export function ConsoleAppV4(props) {
459
+ const windowSize = useWindowSize();
460
+ const size = props.size ?? windowSize;
461
+ const policy = resolveTuiSizePolicy(size.columns, size.rows);
462
+ const [snapshot, setSnapshot] = useState(props.controller.snapshot);
463
+ const [overlay, setOverlay] = useState();
464
+ const [overlayFocus, setOverlayFocus] = useState(0);
465
+ const [discardForExit, setDiscardForExit] = useState(false);
466
+ const [focus, setFocus] = useState(0);
467
+ const [scrollOffset, setScrollOffset] = useState(0);
468
+ const [searchMode, setSearchMode] = useState(false);
469
+ const [query, setQuery] = useState('');
470
+ const [scope, setScope] = useState('user');
471
+ const [tools, setTools] = useState([]);
472
+ const [technicalRouteKey, setTechnicalRouteKey] = useState();
473
+ const [exitAfterBusy, setExitAfterBusy] = useState(false);
474
+ const [, setClockTick] = useState(0);
475
+ const [dirty, setDirty] = useState(false);
476
+ const [configPage, setConfigPage] = useState();
477
+ const [configLayerCounts, setConfigLayerCounts] = useState({});
478
+ const [configLoadKey, setConfigLoadKey] = useState(0);
479
+ const [draftUpdates, setDraftUpdates] = useState({});
480
+ const [draftRemoves, setDraftRemoves] = useState([]);
481
+ const [inputValue, setInputValue] = useState('');
482
+ const [inputError, setInputError] = useState();
483
+ const [configActionError, setConfigActionError] = useState();
484
+ const [configResetAvailability, setConfigResetAvailability] = useState();
485
+ const [actionError, setActionError] = useState();
486
+ const [sourceDraft, setSourceDraft] = useState();
487
+ const [sourceInput, setSourceInput] = useState('');
488
+ const [sourceInputError, setSourceInputError] = useState();
489
+ const [sourceReferenceSummary, setSourceReferenceSummary] = useState();
490
+ const [sourceDependenciesExpanded, setSourceDependenciesExpanded] = useState(false);
491
+ const [portableAgentInput, setPortableAgentInput] = useState('');
492
+ const [draftOwner, setDraftOwner] = useState();
493
+ const exitSent = useRef(false);
494
+ const operationBusy = useRef(false);
495
+ const route = snapshot.router.current.route;
496
+ const routeKey = JSON.stringify(route);
497
+ const showTechnical = technicalRouteKey === routeKey;
498
+ const setShowTechnical = (next) => {
499
+ const visible = typeof next === 'function' ? next(showTechnical) : next;
500
+ setTechnicalRouteKey(visible ? routeKey : undefined);
501
+ };
502
+ const plugin = currentPlugin(props.controller, route);
503
+ const issue = route.name === 'issueDetail'
504
+ ? props.controller.listUserIssues().find((entry) => entry.issueId === route.issueId)
505
+ : undefined;
506
+ const busy = route.name === 'progress';
507
+ useEffect(() => {
508
+ if (!busy)
509
+ return;
510
+ const timer = setInterval(() => setClockTick((value) => value + 1), 1_000);
511
+ return () => clearInterval(timer);
512
+ }, [busy, route.name === 'progress' ? route.operationId : '']);
513
+ const tooSmall = policy.mode === 'too-small';
514
+ const requestExit = (request) => {
515
+ if (exitSent.current)
516
+ return;
517
+ exitSent.current = true;
518
+ props.onExit?.(request);
519
+ };
520
+ useEffect(() => {
521
+ const unsubscribe = props.controller.subscribe(setSnapshot);
522
+ if (props.controller.snapshot.phase === 'idle') {
523
+ void props.controller.load().catch(() => {
524
+ setActionError('操作未完成。请重新检查后重试。');
525
+ });
526
+ }
527
+ return () => {
528
+ unsubscribe();
529
+ if (props.ownsController)
530
+ props.controller.dispose();
531
+ };
532
+ }, [props.controller, props.ownsController]);
533
+ useEffect(() => {
534
+ if (!exitAfterBusy || !snapshot.result)
535
+ return;
536
+ const state = snapshot.result.state;
537
+ requestExit({
538
+ code: state === 'blocked' || state === 'partial' ? 1 : 0,
539
+ reason: state === 'blocked' ? 'blocked' : state === 'partial' ? 'partial'
540
+ : state === 'stale' ? 'stale' : 'completed',
541
+ });
542
+ }, [exitAfterBusy, snapshot.result]);
543
+ useEffect(() => {
544
+ if (route.name !== 'result'
545
+ || !snapshot.result
546
+ || (snapshot.result.state !== 'succeeded' && snapshot.result.state !== 'partial')
547
+ || !snapshot.result.operationKind)
548
+ return;
549
+ setDirty(false);
550
+ setDraftUpdates({});
551
+ setDraftRemoves([]);
552
+ if (snapshot.result.operationKind === 'sourceMutation')
553
+ setSourceDraft(undefined);
554
+ }, [route.name, snapshot.result]);
555
+ useEffect(() => {
556
+ if (route.name !== 'sourceAdd')
557
+ setSourceInput('');
558
+ }, [route.name]);
559
+ useEffect(() => {
560
+ if (route.name === 'portableAgentInput') {
561
+ setPortableAgentInput(snapshot.portableDraft?.requestedAgents.join(' ') ?? '');
562
+ }
563
+ else
564
+ setPortableAgentInput('');
565
+ }, [route.name, route.name === 'portableAgentInput' ? snapshot.portableDraft?.requestedAgents.join('\0') : '']);
566
+ useEffect(() => {
567
+ if (route.name !== 'configFieldEditor')
568
+ setInputValue('');
569
+ }, [route.name]);
570
+ const configRoute = route.name === 'configFields'
571
+ || route.name === 'configFieldEditor'
572
+ || route.name === 'configPresets'
573
+ ? route
574
+ : undefined;
575
+ const currentConfigPage = configPageMatches(configPage, configRoute) ? configPage : undefined;
576
+ useEffect(() => {
577
+ if (route.name !== 'configLayer')
578
+ return;
579
+ const abort = new AbortController();
580
+ const layers = [
581
+ { key: 'effective', layer: { scope: 'effective', projectLayer: 'shared' } },
582
+ { key: 'user', layer: { scope: 'user', projectLayer: 'shared' } },
583
+ { key: 'project', layer: { scope: 'project', projectLayer: 'shared' } },
584
+ { key: 'local', layer: { scope: 'project', projectLayer: 'local' } },
585
+ ];
586
+ void Promise.allSettled(layers.map(async ({ key, layer }) => ({
587
+ key,
588
+ count: (await props.controller.getConfigPage(route.pluginId, layer, abort.signal)).storedCount,
589
+ }))).then((results) => {
590
+ if (abort.signal.aborted)
591
+ return;
592
+ setConfigLayerCounts(Object.fromEntries(results.flatMap((result) => result.status === 'fulfilled' ? [[result.value.key, result.value.count]] : [])));
593
+ });
594
+ return () => abort.abort();
595
+ }, [props.controller, route.name, route.name === 'configLayer' ? route.pluginId : '']);
596
+ useEffect(() => {
597
+ if (!configRoute)
598
+ return;
599
+ const abort = new AbortController();
600
+ const expectedPluginId = configRoute.pluginId;
601
+ const expectedLayer = JSON.stringify(configRoute.layer);
602
+ const owner = configOwnerKey(configRoute);
603
+ setConfigPage(undefined);
604
+ if (draftOwner !== owner) {
605
+ setDraftOwner(owner);
606
+ setDraftUpdates({});
607
+ setDraftRemoves([]);
608
+ setDirty(false);
609
+ setConfigActionError(undefined);
610
+ }
611
+ void props.controller.getConfigPage(expectedPluginId, configRoute.layer, abort.signal)
612
+ .then((page) => {
613
+ if (abort.signal.aborted
614
+ || expectedPluginId !== configRoute.pluginId
615
+ || expectedLayer !== JSON.stringify(configRoute.layer))
616
+ return;
617
+ setConfigPage(page);
618
+ })
619
+ .catch(() => {
620
+ if (!abort.signal.aborted) {
621
+ const layer = configRoute.layer.scope === 'effective'
622
+ ? undefined
623
+ : { scope: configRoute.layer.scope, projectLayer: configRoute.layer.projectLayer };
624
+ props.controller.replace({
625
+ name: 'configUnavailable',
626
+ pluginId: expectedPluginId,
627
+ ...(layer ? { layer } : {}),
628
+ });
629
+ }
630
+ });
631
+ return () => abort.abort();
632
+ }, [configLoadKey, configRoute?.pluginId, configRoute?.layer.scope, configRoute?.layer.projectLayer, props.controller]);
633
+ useEffect(() => {
634
+ if (route.name !== 'configUnavailable' || !route.layer) {
635
+ setConfigResetAvailability(undefined);
636
+ return;
637
+ }
638
+ const abort = new AbortController();
639
+ void props.controller.getConfigResetAvailability(route.pluginId, route.layer)
640
+ .then((availability) => {
641
+ if (!abort.signal.aborted)
642
+ setConfigResetAvailability(availability);
643
+ })
644
+ .catch(() => {
645
+ if (!abort.signal.aborted)
646
+ setConfigResetAvailability({ exists: false, sourceIdentityPresent: false });
647
+ });
648
+ return () => abort.abort();
649
+ }, [props.controller, route.name, route.name === 'configUnavailable' ? route.pluginId : '', route.name === 'configUnavailable' ? JSON.stringify(route.layer) : '']);
650
+ useEffect(() => {
651
+ let count;
652
+ if (route.name === 'sourceList')
653
+ count = props.controller.listSources().length + 1;
654
+ else if (route.name === 'issueList') {
655
+ const issueCount = props.controller.listUserIssues().length;
656
+ count = issueCount || 2;
657
+ }
658
+ else if (route.name === 'pluginList') {
659
+ const itemCount = props.controller.listPlugins({
660
+ mode: route.mode,
661
+ ...(route.sourceId ? { sourceId: route.sourceId } : {}),
662
+ ...(query ? { query } : {}),
663
+ }).length;
664
+ count = itemCount || 1;
665
+ }
666
+ if (count === undefined)
667
+ return;
668
+ setFocus((value) => Math.min(value, Math.max(0, count - 1)));
669
+ const maximumVisible = policy.mode === 'compact' ? 4 : 6;
670
+ setScrollOffset((value) => Math.min(value, Math.max(0, count - maximumVisible)));
671
+ }, [
672
+ policy.mode,
673
+ props.controller,
674
+ query,
675
+ route.name,
676
+ route.name === 'pluginList' ? route.mode : '',
677
+ route.name === 'pluginList' ? route.sourceId : '',
678
+ snapshot.viewModel,
679
+ ]);
680
+ const activeField = route.name === 'configFieldEditor'
681
+ ? currentConfigPage?.groups.flatMap((group) => group.fields).find((field) => field.key === route.fieldKey)
682
+ : undefined;
683
+ useEffect(() => {
684
+ if (route.name !== 'configFieldEditor' || !activeField)
685
+ return;
686
+ const draft = draftUpdates[activeField.key];
687
+ setInputValue(route.editor === 'sensitive' || route.editor === 'path'
688
+ ? draft === undefined ? '' : String(draft)
689
+ : String(draft ?? activeField.value ?? ''));
690
+ setInputError(undefined);
691
+ }, [route.name, route.name === 'configFieldEditor' ? route.fieldKey : '', activeField?.key]);
692
+ useEffect(() => {
693
+ if (route.name !== 'sourceAdd')
694
+ return;
695
+ const draft = sourceDraft?.owner === route.owner ? sourceDraft : undefined;
696
+ setSourceInput(route.step === 'id' ? draft?.sourceId ?? ''
697
+ : route.step === 'location' ? draft?.location ?? ''
698
+ : route.step === 'ref' ? draft?.ref ?? ''
699
+ : route.step === 'subdir' ? draft?.subdir ?? '' : '');
700
+ setSourceInputError(undefined);
701
+ }, [route.name, route.name === 'sourceAdd' ? `${route.owner}:${route.step}` : '', sourceDraft?.owner]);
702
+ useEffect(() => {
703
+ if (route.name !== 'sourceRemoveBlocked')
704
+ return;
705
+ const abort = new AbortController();
706
+ setSourceReferenceSummary(undefined);
707
+ setSourceDependenciesExpanded(false);
708
+ void props.controller.inspectSourceRemoval(route.sourceId)
709
+ .then((summary) => { if (!abort.signal.aborted)
710
+ setSourceReferenceSummary(summary); })
711
+ .catch(() => { if (!abort.signal.aborted)
712
+ setSourceReferenceSummary({ blocked: true, inventoryComplete: false, dependencies: [] }); });
713
+ return () => abort.abort();
714
+ }, [props.controller, route.name, route.name === 'sourceRemoveBlocked' ? route.sourceId : '']);
715
+ const screenId = resolveConsoleV4ScreenId(snapshot, props.controller, overlay, tooSmall, searchMode);
716
+ useEffect(() => {
717
+ props.onScreenChange?.(screenId);
718
+ }, [props.onScreenChange, screenId]);
719
+ const selectedPlatforms = (candidate, selectedScope) => (candidate?.supportedPlatforms ?? []).map((platform) => {
720
+ const placement = props.controller.getPlatformPlacement(candidate.pluginId, platform);
721
+ if (!placement)
722
+ return { platform, disabled: '当前没有可信的平台能力信息' };
723
+ if (placement.state === 'scopeConflict')
724
+ return { platform, disabled: '安装位置冲突;请先处理问题' };
725
+ if (placement.state === 'installed') {
726
+ return { platform, disabled: `已安装到${scopeLabel(placement.scope)};请先移除现有安装` };
727
+ }
728
+ if (!placement.supportedScopes.includes(selectedScope))
729
+ return { platform, disabled: '不支持所选位置' };
730
+ const overview = props.controller.getPluginDependencyOverview(candidate.pluginId);
731
+ for (const dependency of overview?.dependencies ?? []) {
732
+ const dependentPlugin = props.controller.getPluginOverview(dependency.pluginId);
733
+ if (!dependentPlugin || !dependentPlugin.supportedPlatforms.includes(platform)) {
734
+ return { platform, disabled: `${dependency.displayName} 不支持${platformLabel(platform)}` };
735
+ }
736
+ const dependencyPlacement = props.controller.getPlatformPlacement(dependency.pluginId, platform);
737
+ if (dependencyPlacement?.state === 'scopeConflict') {
738
+ return { platform, disabled: `${dependency.displayName} 的安装位置冲突` };
739
+ }
740
+ if (dependencyPlacement?.state === 'installed') {
741
+ if (dependencyPlacement.health !== 'ready') {
742
+ return { platform, disabled: `${dependency.displayName} 需要先处理` };
743
+ }
744
+ if (selectedScope === 'user' && dependencyPlacement.scope === 'project') {
745
+ return { platform, disabled: `${dependency.displayName} 只在当前项目可用` };
746
+ }
747
+ }
748
+ if (dependencyPlacement?.state === 'notInstalled'
749
+ && !dependencyPlacement.supportedScopes.includes(selectedScope)) {
750
+ return { platform, disabled: `${dependency.displayName} 不支持所选位置` };
751
+ }
752
+ }
753
+ return { platform };
754
+ });
755
+ const dependencyOverview = (candidate) => candidate ? props.controller.getPluginDependencyOverview(candidate.pluginId) : undefined;
756
+ const installIntent = (candidate) => ({
757
+ kind: 'pluginWrite',
758
+ action: 'install',
759
+ pluginId: candidate.pluginId,
760
+ platform: tools.length === 2 ? 'both' : tools[0],
761
+ scope,
762
+ });
763
+ let title = '管理你的 Agent 工作流插件';
764
+ let description = '为 Codex 和 Claude Code 添加、设置或移除开发能力。';
765
+ let breadcrumb = '首页';
766
+ let context;
767
+ let help = '↑↓ 移动 · Enter 选择 · ? 帮助 · q 退出';
768
+ let content;
769
+ let actions = [];
770
+ const go = (next) => {
771
+ props.controller.updatePageContext({ focusIndex: focus, scrollOffset, query, sourceTabId: route.name === 'pluginList' ? route.sourceId ?? 'all' : undefined });
772
+ props.controller.push(next);
773
+ setFocus(0);
774
+ setScrollOffset(0);
775
+ setSearchMode(false);
776
+ setShowTechnical(false);
777
+ setActionError(undefined);
778
+ };
779
+ const back = () => {
780
+ if (route.name === 'result')
781
+ props.controller.leaveResult();
782
+ else
783
+ props.controller.back();
784
+ const restored = props.controller.snapshot.router.current.context;
785
+ setFocus(restored.focusIndex ?? 0);
786
+ setScrollOffset(restored.scrollOffset ?? 0);
787
+ setQuery(restored.query ?? '');
788
+ setSearchMode(Boolean(restored.query));
789
+ setActionError(undefined);
790
+ };
791
+ const leaveResult = (target) => {
792
+ props.controller.leaveResult(target);
793
+ const restored = props.controller.snapshot.router.current.context;
794
+ setFocus(restored.focusIndex ?? 0);
795
+ setScrollOffset(restored.scrollOffset ?? 0);
796
+ setQuery(restored.query ?? '');
797
+ setSearchMode(Boolean(restored.query));
798
+ setShowTechnical(false);
799
+ setActionError(undefined);
800
+ };
801
+ const beginReview = async (intent) => {
802
+ const underlyingResultRoute = route.name === 'result' && snapshot.result && 'returnRoute' in snapshot.result
803
+ ? snapshot.result.returnRoute
804
+ : undefined;
805
+ await props.controller.buildOperationReview(intent, underlyingResultRoute);
806
+ setFocus(0);
807
+ setScrollOffset(0);
808
+ };
809
+ const beginPortable = async (pluginId) => {
810
+ try {
811
+ await props.controller.preparePortableInstall(pluginId, route.name === 'portableBlocker' ? 'replace' : 'push');
812
+ setFocus(0);
813
+ setScrollOffset(0);
814
+ setActionError(undefined);
815
+ }
816
+ catch (error) {
817
+ if (error instanceof Error && error.name === 'AbortError')
818
+ return;
819
+ const blocker = extractPortableError(error);
820
+ const target = { name: 'portableBlocker', pluginId, ...blocker };
821
+ if (props.controller.snapshot.router.current.route.name === 'portableBlocker') {
822
+ props.controller.replace(target);
823
+ }
824
+ else {
825
+ go(target);
826
+ }
827
+ }
828
+ };
829
+ const beginPortableReview = async (pluginId, selectedScope) => {
830
+ props.controller.setPortableScope(selectedScope);
831
+ try {
832
+ await props.controller.buildPortableInstallReview();
833
+ setFocus(0);
834
+ setActionError(undefined);
835
+ }
836
+ catch (error) {
837
+ if (error instanceof Error && error.name === 'AbortError')
838
+ return;
839
+ const blocker = extractPortableError(error);
840
+ go({ name: 'portableBlocker', pluginId, ...blocker });
841
+ }
842
+ };
843
+ const refreshPortableDraft = async (pluginId) => {
844
+ try {
845
+ await props.controller.refreshPortableInstallDraft(pluginId);
846
+ return true;
847
+ }
848
+ catch (error) {
849
+ if (error instanceof Error && error.name === 'AbortError')
850
+ return false;
851
+ const blocker = extractPortableError(error);
852
+ const target = { name: 'portableBlocker', pluginId, ...blocker };
853
+ if (route.name === 'result')
854
+ leaveResult(target);
855
+ else
856
+ props.controller.replace(target);
857
+ setFocus(0);
858
+ setScrollOffset(0);
859
+ return false;
860
+ }
861
+ };
862
+ const beginPluginRemoval = (candidate, platform, selectedScope) => {
863
+ if (props.controller.getBlockingDependents(candidate.pluginId, platform, selectedScope).length > 0) {
864
+ go({ name: 'pluginDependencies', pluginId: candidate.pluginId, view: 'removeBlocked',
865
+ platform, scope: selectedScope });
866
+ return;
867
+ }
868
+ void beginReview({
869
+ kind: 'pluginWrite',
870
+ action: 'uninstall',
871
+ pluginId: candidate.pluginId,
872
+ platform,
873
+ scope: selectedScope,
874
+ });
875
+ };
876
+ const beginAddSource = () => {
877
+ const owner = `source-add:${Date.now()}`;
878
+ setSourceDraft({ owner, mode: 'add', sourceId: '', type: 'git', location: '', refPolicy: 'cliChannel', ref: '', subdir: '' });
879
+ setDirty(true);
880
+ go({ name: 'sourceAdd', step: 'type', owner });
881
+ };
882
+ const beginIssueSourceAdd = (sourceId) => {
883
+ const owner = `source-readd:${sourceId}:${Date.now()}`;
884
+ setSourceDraft({ owner, mode: 'add', sourceId, type: 'git', location: '', refPolicy: 'cliChannel', ref: '', subdir: '' });
885
+ setDirty(true);
886
+ go({ name: 'sourceAdd', step: 'type', owner });
887
+ };
888
+ const issueWriteIntent = (selectedIssue, action) => {
889
+ const pluginId = action.pluginId ?? selectedIssue.pluginId;
890
+ if (!pluginId || !selectedIssue.platform)
891
+ return undefined;
892
+ const selectedScope = action.id === 'keepUserScope'
893
+ ? 'project'
894
+ : action.id === 'keepProjectScope' ? 'user' : action.scope ?? selectedIssue.scope;
895
+ const selectedPlatform = action.platform ?? selectedIssue.platform;
896
+ const writeAction = action.writeAction ?? (action.id === 'reinstall'
897
+ ? 'reinstall'
898
+ : action.id === 'remove' || action.id === 'keepUserScope' || action.id === 'keepProjectScope'
899
+ ? 'uninstall'
900
+ : undefined);
901
+ if (!selectedScope || !selectedPlatform || !writeAction)
902
+ return undefined;
903
+ return {
904
+ kind: 'pluginWrite',
905
+ action: writeAction,
906
+ pluginId,
907
+ platform: selectedPlatform,
908
+ scope: selectedScope,
909
+ originIssue: {
910
+ issueId: selectedIssue.issueId,
911
+ issueKey: userIssueKey(selectedIssue),
912
+ category: selectedIssue.category,
913
+ actionId: action.id,
914
+ ...(selectedIssue.category === 'dependencyMissing' || selectedIssue.category === 'dependencyNotReady'
915
+ ? {
916
+ consumerPluginId: selectedIssue.pluginId,
917
+ consumerSourceIdentity: selectedIssue.sourceIdentity,
918
+ consumerPlatform: selectedIssue.platform,
919
+ consumerScope: selectedIssue.scope,
920
+ dependencyPluginId: selectedIssue.dependencyPluginId,
921
+ dependencyPlatform: selectedIssue.dependencyPlatform,
922
+ dependencyScope: selectedIssue.dependencyScope,
923
+ dependencyProject: selectedIssue.dependencyProject,
924
+ }
925
+ : {}),
926
+ },
927
+ };
928
+ };
929
+ const sourceFromDraft = (draft) => draft.type === 'git'
930
+ ? {
931
+ id: draft.sourceId,
932
+ type: 'git',
933
+ primary: false,
934
+ url: draft.location,
935
+ refPolicy: draft.refPolicy === 'cliChannel' ? { type: 'cliChannel' } : { type: 'explicit', ref: draft.ref },
936
+ ...(draft.subdir ? { subdir: draft.subdir } : {}),
937
+ }
938
+ : { id: draft.sourceId, type: 'path', primary: false, path: draft.location };
939
+ const beginSourceAddReviewWith = async (draft) => {
940
+ const source = sourceFromDraft(draft);
941
+ await beginReview({
942
+ kind: 'sourceMutation', action: 'add', sourceId: source.id, source,
943
+ changes: [
944
+ { title: `添加来源 ${source.id}`, after: source.type === 'git' ? 'Git 仓库' : '本地目录' },
945
+ { title: source.type === 'git' ? 'Git URL' : '本地目录', ...(source.type === 'git' ? { after: projectCatalogSourceLocation(source.url) } : { private: true }) },
946
+ ...(source.type === 'git' ? [{ title: '版本引用', after: source.refPolicy.type === 'cliChannel' ? '跟随 AW 版本通道' : source.refPolicy.ref }] : []),
947
+ ...(source.type === 'git' && source.subdir ? [{ title: 'Catalog 子目录', after: source.subdir }] : []),
948
+ ],
949
+ });
950
+ };
951
+ const beginSourceUpdateReview = async (draft) => {
952
+ const detail = props.controller.getSourceOverview(draft.sourceId).detail;
953
+ if (!detail || !draft.editField)
954
+ return;
955
+ const update = draft.type === 'path'
956
+ ? { type: 'path', path: draft.location }
957
+ : draft.editField === 'location'
958
+ ? { type: 'git', url: draft.location }
959
+ : draft.editField === 'refPolicy'
960
+ ? { type: 'git', refPolicy: draft.refPolicy === 'cliChannel' ? { type: 'cliChannel' } : { type: 'explicit', ref: draft.ref } }
961
+ : { type: 'git', subdir: draft.subdir || null };
962
+ const change = draft.editField === 'location'
963
+ ? { title: draft.type === 'git' ? 'Git URL' : '本地目录', before: detail.locationSummary, ...(draft.type === 'git' ? { after: projectCatalogSourceLocation(draft.location) } : { private: true }) }
964
+ : draft.editField === 'refPolicy'
965
+ ? { title: '版本引用', before: detail.refPolicy === 'explicit' ? detail.explicitRef : '跟随 AW 版本通道', after: draft.refPolicy === 'explicit' ? draft.ref : '跟随 AW 版本通道' }
966
+ : { title: 'Catalog 子目录', before: detail.subdir ?? '未设置', after: draft.subdir || '未设置' };
967
+ await beginReview({ kind: 'sourceMutation', action: 'update', sourceId: draft.sourceId, update, changes: [change] });
968
+ };
969
+ const configLayerLabel = (layer) => layer.scope === 'effective' ? '有效设置'
970
+ : layer.scope === 'user' ? '个人默认'
971
+ : layer.projectLayer === 'local' ? '当前项目(仅本机)' : '当前项目';
972
+ const currentFieldDisplay = (field) => {
973
+ if (draftRemoves.includes(field.key))
974
+ return '恢复继承';
975
+ if (Object.hasOwn(draftUpdates, field.key)) {
976
+ const value = draftUpdates[field.key];
977
+ if (field.sensitive || field.type === 'file' || field.type === 'directory')
978
+ return '将更新';
979
+ if (field.type === 'enum')
980
+ return field.valueLabels?.[String(value)] ?? String(value);
981
+ if (field.type === 'boolean')
982
+ return value ? '开启' : '关闭';
983
+ return `${String(value)}${field.unit ? ` ${field.unit}` : ''}`;
984
+ }
985
+ if (field.type === 'enum' && field.value !== undefined) {
986
+ return field.valueLabels?.[String(field.value)] ?? field.displayValue;
987
+ }
988
+ if (field.type === 'boolean' && field.value !== undefined)
989
+ return field.value ? '开启' : '关闭';
990
+ return `${field.displayValue}${field.unit && field.displayValue !== '未设置' ? ` ${field.unit}` : ''}`;
991
+ };
992
+ const fieldEditor = (field) => {
993
+ if (field.sensitive)
994
+ return 'sensitive';
995
+ if (field.type === 'file' || field.type === 'directory')
996
+ return 'path';
997
+ return field.type;
998
+ };
999
+ const updateDraft = (key, value) => {
1000
+ setConfigActionError(undefined);
1001
+ const field = currentConfigPage?.groups.flatMap((group) => group.fields)
1002
+ .find((candidate) => candidate.key === key);
1003
+ const unchanged = field?.configured === true && field.value === value;
1004
+ setDraftUpdates((current) => {
1005
+ const next = { ...current };
1006
+ if (unchanged)
1007
+ delete next[key];
1008
+ else
1009
+ next[key] = value;
1010
+ const nextRemoves = draftRemoves.filter((candidate) => candidate !== key);
1011
+ setDraftRemoves(nextRemoves);
1012
+ setDirty(Object.keys(next).length + nextRemoves.length > 0);
1013
+ return next;
1014
+ });
1015
+ };
1016
+ const removeDraft = (key) => {
1017
+ setConfigActionError(undefined);
1018
+ setDraftUpdates((current) => {
1019
+ const next = { ...current };
1020
+ delete next[key];
1021
+ const field = currentConfigPage?.groups.flatMap((group) => group.fields)
1022
+ .find((candidate) => candidate.key === key);
1023
+ const nextRemoves = field?.configured
1024
+ ? [...new Set([...draftRemoves, key])]
1025
+ : draftRemoves.filter((candidate) => candidate !== key);
1026
+ setDraftRemoves(nextRemoves);
1027
+ setDirty(Object.keys(next).length + nextRemoves.length > 0);
1028
+ return next;
1029
+ });
1030
+ };
1031
+ const configChanges = () => {
1032
+ const fields = currentConfigPage?.groups.flatMap((group) => group.fields) ?? [];
1033
+ return [...Object.keys(draftUpdates), ...draftRemoves.filter((key) => !Object.hasOwn(draftUpdates, key))]
1034
+ .map((key) => {
1035
+ const field = fields.find((candidate) => candidate.key === key);
1036
+ return {
1037
+ key,
1038
+ title: field?.title ?? key,
1039
+ before: field?.displayValue ?? '未设置',
1040
+ after: draftRemoves.includes(key) ? '未设置' : currentFieldDisplay(field),
1041
+ };
1042
+ });
1043
+ };
1044
+ const beginConfigSave = async () => {
1045
+ if (!configRoute || configRoute.layer.scope === 'effective' || !currentConfigPage || !dirty)
1046
+ return;
1047
+ const fields = currentConfigPage.groups.flatMap((group) => group.fields);
1048
+ try {
1049
+ await beginReview({
1050
+ kind: 'configSave',
1051
+ pluginId: configRoute.pluginId,
1052
+ scope: configRoute.layer.scope,
1053
+ projectLayer: configRoute.layer.projectLayer,
1054
+ updates: draftUpdates,
1055
+ removeKeys: draftRemoves,
1056
+ sensitiveKeys: fields.filter((field) => field.sensitive).map((field) => field.key),
1057
+ privateKeys: fields.filter((field) => field.type === 'file' || field.type === 'directory').map((field) => field.key),
1058
+ changes: configChanges(),
1059
+ });
1060
+ setConfigActionError(undefined);
1061
+ }
1062
+ catch {
1063
+ setConfigActionError('设置值未通过验证。请重新检查修改。');
1064
+ }
1065
+ };
1066
+ if (snapshot.phase === 'error') {
1067
+ breadcrumb = '启动失败';
1068
+ title = '无法读取插件状态';
1069
+ description = snapshot.loadError?.message ?? '当前不能安全显示插件状态。';
1070
+ help = 'q 退出 · Ctrl-C 退出';
1071
+ content = _jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", snapshot.loadError?.code ?? 'statusLoadFailed'] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u6CA1\u6709\u6267\u884C\u4EFB\u4F55\u5199\u64CD\u4F5C\u3002" })] });
1072
+ }
1073
+ else if (snapshot.phase !== 'ready') {
1074
+ breadcrumb = '启动';
1075
+ title = '正在检查插件状态';
1076
+ description = '单个来源失败不会阻止其他来源继续加载。';
1077
+ help = 'Ctrl-C 完成后退出';
1078
+ content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: tuiTheme.statusColors.success, children: "\u2713 \u8BFB\u53D6\u63D2\u4EF6\u6765\u6E90" }), _jsx(Text, { color: tuiTheme.colors.accent, children: "\u25CF \u68C0\u67E5\u5F53\u524D\u9879\u76EE" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u25CB \u6C47\u603B\u53EF\u7528\u64CD\u4F5C" })] }));
1079
+ }
1080
+ else if (route.name === 'home') {
1081
+ const home = snapshot.viewModel.home;
1082
+ context = '当前项目';
1083
+ actions = [
1084
+ { label: '添加插件', description: '浏览多个来源中的 Agent 能力', meta: String(home.availablePlugins), run: () => go({ name: 'pluginList', mode: 'browse' }) },
1085
+ { label: '我的插件', description: '查看状态、设置和移除已添加插件', meta: String(home.installedPlugins), run: () => go({ name: 'pluginList', mode: 'installed' }) },
1086
+ { label: '需要处理', description: '解决安装、来源或设置问题', meta: String(home.issueCount), status: 'warning', run: () => go({ name: 'issueList' }) },
1087
+ { label: '插件来源', description: '管理 Git 仓库和本地目录', meta: String(home.sourceCount), run: () => go({ name: 'sourceList' }) },
1088
+ ];
1089
+ content = (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsxs(Text, { color: tuiTheme.statusColors.success, children: ["\u2713 ", home.installedPlugins, " \u4E2A\u63D2\u4EF6\u53EF\u7528"] }), ' ', _jsxs(Text, { color: tuiTheme.statusColors.warning, children: ["! ", home.issueCount, " \u9879\u9700\u8981\u5904\u7406"] }), home.cachedSourceCount > 0 ? _jsxs(_Fragment, { children: [' ', _jsxs(Text, { color: tuiTheme.statusColors.warning, children: ["\u25D0 ", home.cachedSourceCount, " \u4E2A\u6765\u6E90\u4F7F\u7528\u7F13\u5B58"] })] }) : null] }), home.unavailableSourceCount > 0 ? _jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", home.unavailableSourceCount, " \u4E2A\u6765\u6E90\u4E0D\u53EF\u7528"] }) : null, _jsx(Box, { marginTop: 1, children: _jsx(ActionRows, { actions: actions, focus: focus }) })] }));
1090
+ }
1091
+ else if (route.name === 'pluginList') {
1092
+ const sourceId = route.sourceId ?? 'all';
1093
+ const items = props.controller.listPlugins({
1094
+ mode: route.mode,
1095
+ ...(sourceId !== 'all' ? { sourceId } : {}),
1096
+ ...(query ? { query } : {}),
1097
+ });
1098
+ breadcrumb = route.mode === 'browse' ? '添加插件' : '我的插件';
1099
+ title = breadcrumb;
1100
+ description = route.mode === 'browse' ? '选择希望 Agent 获得的能力。' : '查看状态,或选择一个插件进行管理。';
1101
+ context = `${items.length} 个插件`;
1102
+ help = '↑↓ 移动 · Enter 查看 · ←→ 来源 · / 搜索 · Esc 返回';
1103
+ const sourceError = sourceId !== 'all' && props.controller.getSourceOverview(sourceId).source?.state === 'error';
1104
+ actions = sourceError
1105
+ ? [{ label: '查看来源问题', description: '没有可用缓存;当前不能浏览这个来源', run: () => go({ name: 'issueDetail', issueId: `source:${sourceId}:unavailable` }) }]
1106
+ : items.map((item) => ({
1107
+ label: item.displayName,
1108
+ description: `${item.description}${sourceId === 'all' ? ` · ${item.sourceLabel}` : ''}`,
1109
+ meta: statusText(item),
1110
+ status: item.aggregateStatus === 'needsAttention' ? 'warning'
1111
+ : item.aggregateStatus === 'installed' ? 'success'
1112
+ : item.aggregateStatus === 'unavailable' ? 'error' : 'muted',
1113
+ run: () => go(route.mode === 'browse'
1114
+ ? { name: 'pluginPreview', pluginId: item.pluginId }
1115
+ : { name: 'pluginDetail', pluginId: item.pluginId }),
1116
+ }));
1117
+ if (route.mode === 'installed' && items.length === 0 && !sourceError) {
1118
+ title = '还没有添加插件';
1119
+ description = `从已配置的 ${props.controller.listSources().length} 个来源中浏览可用能力。`;
1120
+ actions = [{ label: '浏览插件', description: '开始添加第一个插件', run: () => go({ name: 'pluginList', mode: 'browse' }) }];
1121
+ }
1122
+ const viewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
1123
+ content = (_jsxs(Box, { flexDirection: "column", children: [_jsx(SourceTabs, { controller: props.controller, current: sourceId, compact: policy.mode === 'compact', mode: route.mode }), _jsxs(Text, { color: searchMode ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: ["/ ", searchMode ? query || '输入搜索内容' : '搜索名称、用途或来源…'] }), sourceError
1124
+ ? _jsxs(Bordered, { children: [_jsxs(Text, { bold: true, children: [sourceId, " \u6682\u65F6\u65E0\u6CD5\u63D0\u4F9B\u63D2\u4EF6"] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u6CA1\u6709\u53EF\u7528\u7F13\u5B58\uFF0C\u56E0\u6B64\u5F53\u524D\u4E0D\u80FD\u6D4F\u89C8\u8FD9\u4E2A\u6765\u6E90\u3002" })] })
1125
+ : route.mode === 'installed' && items.length === 0
1126
+ ? _jsxs(Bordered, { children: [_jsx(Text, { bold: true, children: "\u5F00\u59CB\u6DFB\u52A0\u7B2C\u4E00\u4E2A\u63D2\u4EF6" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u63D2\u4EF6\u53EF\u4EE5\u5B89\u88C5\u5230 Codex\u3001Claude Code \u6216\u4E24\u8005\u3002" })] })
1127
+ : null, _jsx(ActionRows, { actions: actions, focus: focus, offset: viewport.offset, limit: viewport.limit })] }));
1128
+ }
1129
+ else if (route.name === 'pluginPreview' && plugin) {
1130
+ breadcrumb = `添加插件 / ${plugin.displayName}`;
1131
+ context = plugin.sourceLabel;
1132
+ title = `${plugin.displayName}${plugin.version ? ` ${plugin.version}` : ''}`;
1133
+ description = plugin.description;
1134
+ const overview = dependencyOverview(plugin);
1135
+ const hasDependencies = (plugin.dependencySummary?.totalCount ?? 0) > 0;
1136
+ const beginInstall = () => {
1137
+ const initialScope = ['user', 'project'].find((candidateScope) => selectedPlatforms(plugin, candidateScope).some((entry) => !entry.disabled)) ?? scope;
1138
+ const available = selectedPlatforms(plugin, initialScope)
1139
+ .filter((entry) => !entry.disabled).map((entry) => entry.platform);
1140
+ setScope(initialScope);
1141
+ setTools(available);
1142
+ if (available.length > 0)
1143
+ go({ name: 'installPlacement', pluginId: plugin.pluginId });
1144
+ else
1145
+ go({ name: 'installTools', pluginId: plugin.pluginId, view: 'blocked' });
1146
+ };
1147
+ const portableAction = plugin.portableSkills
1148
+ ? [{ label: '安装通用 Skills…', description: '选择 Skill、Agent 和使用范围', run: () => beginPortable(plugin.pluginId) }]
1149
+ : [];
1150
+ actions = hasDependencies && (overview?.blockers.length ?? 0) > 0
1151
+ ? [
1152
+ { label: '查看依赖问题', description: overview.blockers[0].explanation, status: 'warning', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) },
1153
+ ...portableAction,
1154
+ { label: '查看依赖关系', description: '查看直接、间接依赖和当前状态', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) },
1155
+ { label: '查看插件设置定义', description: `${plugin.configFieldCount} 个配置字段`, run: () => go({ name: 'configLayer', pluginId: plugin.pluginId }) },
1156
+ ]
1157
+ : [
1158
+ { label: '添加插件', description: '选择使用位置并确认变化', run: beginInstall },
1159
+ ...portableAction,
1160
+ ...(hasDependencies ? [{ label: '查看依赖关系', description: '查看直接、间接依赖和当前状态', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) }] : []),
1161
+ { label: '查看插件设置定义', description: `${plugin.configFieldCount} 个配置字段`, run: () => go({ name: 'configLayer', pluginId: plugin.pluginId }) },
1162
+ { label: '查看来源', run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }) },
1163
+ ];
1164
+ const summaryRows = (relation) => {
1165
+ const dependencies = overview?.dependencies.filter((entry) => entry.relation === relation) ?? [];
1166
+ const visible = dependencies.slice(0, 3);
1167
+ return [
1168
+ ...visible.map((entry) => _jsxs(Text, { children: [_jsxs(Text, { color: relation === 'direct' ? tuiTheme.colors.text : tuiTheme.colors.muted, children: ["\u2022 ", entry.displayName] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: [" ", relation === 'direct' ? '直接依赖' : '间接依赖'] })] }, entry.pluginId)),
1169
+ ...(dependencies.length > visible.length
1170
+ ? [_jsxs(Text, { color: tuiTheme.colors.muted, children: [" \u53E6\u6709 ", dependencies.length - visible.length, " \u4E2A", relation === 'direct' ? '直接' : '间接', "\u4F9D\u8D56"] }, `${relation}:more`)]
1171
+ : []),
1172
+ ];
1173
+ };
1174
+ content = hasDependencies
1175
+ ? _jsxs(Box, { flexDirection: "column", children: [plugin.portableSkills ? _jsx(Bordered, { title: "\u53EF\u7528\u65B9\u5F0F", children: _jsx(Text, { children: "\u901A\u7528 Skills \u63D0\u4F9B\u901A\u7528 Skills" }) }) : null, _jsxs(Bordered, { title: "\u5B89\u88C5\u5185\u5BB9", children: [_jsxs(Text, { children: [_jsxs(Text, { color: tuiTheme.colors.accent, children: ["\u2022 ", plugin.displayName] }), _jsx(Text, { color: tuiTheme.colors.muted, children: " \u4F60\u9009\u62E9\u7684\u63D2\u4EF6" })] }), summaryRows('direct'), summaryRows('transitive'), _jsxs(Text, { color: tuiTheme.colors.muted, children: [plugin.dependencySummary.totalCount + 1, " \u4E2A\u63D2\u4EF6\u5C06\u4F5C\u4E3A\u4E00\u4E2A\u5B89\u88C5\u8BA1\u5212\u68C0\u67E5\u3002"] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u4F9D\u8D56\u4ECE\u540C\u4E00\u4E2A\u6765\u6E90\u5FEB\u7167\u89E3\u6790\u3002" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] })
1176
+ : _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { title: "\u53EF\u7528\u5DE5\u5177", children: [_jsx(Text, { children: plugin.supportedPlatforms.map(platformLabel).join(' · ') || '暂无可用工具' }), plugin.portableSkills ? _jsx(Text, { children: "\u901A\u7528 Skills \u63D0\u4F9B\u901A\u7528 Skills" }) : null] }), _jsxs(Bordered, { title: "\u4F7F\u7528\u8BF4\u660E", children: [_jsx(Text, { children: "\u2713 \u5728 Agent \u4F1A\u8BDD\u4E2D\u63D0\u4F9B\u80FD\u529B" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5B89\u88C5\u4E0D\u4F1A\u81EA\u52A8\u4FEE\u6539\u9879\u76EE\u6E90\u7801\u3002" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1177
+ }
1178
+ else if (route.name === 'portableSkillSelect' && plugin && snapshot.portableDraft) {
1179
+ const draft = snapshot.portableDraft;
1180
+ const focusedCandidate = draft.candidates[focus];
1181
+ breadcrumb = `${plugin.displayName} / 通用 Skills`;
1182
+ context = `${draft.selectedCount} / ${draft.totalCount} 已选择`;
1183
+ title = '选择要安装的 Skills';
1184
+ description = '已根据插件依赖和当前设置准备候选;默认全部选择。';
1185
+ const candidateActions = draft.candidates.map((candidate) => ({
1186
+ label: `${candidate.selected ? '◆' : '◇'} ${candidate.name}`,
1187
+ description: portableScopeVariantsDiffer(candidate)
1188
+ ? '当前项目与所有项目提供不同内容;下方显示两种范围变体'
1189
+ : `${candidate.description} · ${candidate.providerDisplayName} · ${candidate.relation === 'requested' ? '当前插件' : candidate.relation === 'direct' ? '直接依赖' : '间接依赖'}${candidate.dynamic ? ' · 按设置生成' : ''}`,
1190
+ run: () => props.controller.togglePortableSkill(candidate.name),
1191
+ }));
1192
+ actions = [
1193
+ ...candidateActions,
1194
+ { label: '继续', disabled: draft.canContinue ? undefined : '至少选择一个 Skill', run: () => go({ name: 'portableAgentMode', pluginId: plugin.pluginId }) },
1195
+ ...(plugin.dependencySummary?.totalCount ? [{ label: '查看依赖关系', run: () => go({ name: 'portableDependencies', pluginId: plugin.pluginId }) }] : []),
1196
+ ];
1197
+ const viewport = portableSkillCandidateViewport(candidateActions, draft.candidates, policy.mode, focus, scrollOffset);
1198
+ help = '↑↓ 移动 · Space 切换 · a 全选 · Enter 继续 · Esc 返回';
1199
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(PortableSkillRows, { actions: candidateActions, candidates: draft.candidates, focus: focus, offset: viewport.offset, limit: viewport.limit }), focusedCandidate && portableScopeVariantsDiffer(focusedCandidate) ? _jsxs(Bordered, { title: "\u8303\u56F4\u53D8\u4F53", children: [_jsx(Text, { children: portableScopeVariantLabel('project', focusedCandidate.scopeDetails.project) }), _jsx(Text, { children: portableScopeVariantLabel('user', focusedCandidate.scopeDetails.user) })] }) : null, _jsx(ActionRows, { actions: actions, focus: focus, offset: draft.candidates.length, limit: actions.length - draft.candidates.length })] });
1200
+ }
1201
+ else if (route.name === 'portableDependencies' && plugin && snapshot.portableDraft) {
1202
+ const overview = dependencyOverview(plugin);
1203
+ breadcrumb = `${plugin.displayName} / 通用 Skills / 依赖`;
1204
+ context = `${(overview?.summary.totalCount ?? 0) + 1} 个插件`;
1205
+ title = '生成这些候选 Skills 需要的插件';
1206
+ description = '插件依赖用于生成,不能在这里取消。';
1207
+ actions = [
1208
+ ...(overview?.dependencies ?? []).map((dependency) => ({
1209
+ label: dependency.displayName,
1210
+ description: `${dependency.relation === 'direct' ? '直接依赖' : '间接依赖'} · 贡献 ${snapshot.portableDraft.candidates.filter((candidate) => candidate.providerPluginId === dependency.pluginId).length} 个候选 Skill`,
1211
+ })),
1212
+ { label: '返回 Skill 选择', run: back },
1213
+ { label: '技术详情', run: () => setShowTechnical((value) => !value) },
1214
+ ];
1215
+ const viewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
1216
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { title: "\u5F53\u524D\u63D2\u4EF6", children: _jsxs(Text, { children: [plugin.displayName, " \u00B7 \u8D21\u732E ", snapshot.portableDraft.candidates.filter((candidate) => candidate.relation === 'requested').length, " \u4E2A\u5019\u9009 Skills"] }) }), _jsx(ActionRows, { actions: actions, focus: focus, offset: viewport.offset, limit: viewport.limit }), showTechnical ? _jsx(Text, { color: tuiTheme.colors.muted, children: overview?.dependencies.map((entry) => entry.pluginId).join('\n') }) : null] });
1217
+ }
1218
+ else if (route.name === 'portableAgentMode' && plugin && snapshot.portableDraft) {
1219
+ breadcrumb = `${plugin.displayName} / 通用 Skills / Agent`;
1220
+ title = '安装到哪些 Agent?';
1221
+ description = '可以自动选择检测到的 Agent,也可以限制到指定 Agent。';
1222
+ actions = [
1223
+ { label: '自动选择', description: '自动选择检测到的 Agent;未检测到时可能选择全部 Agent', meta: '默认', run: () => { props.controller.setPortableAgentMode('automatic'); go({ name: 'portableScope', pluginId: plugin.pluginId }); } },
1224
+ { label: '指定 Agent…', description: '输入一个或多个 Skills CLI ID', run: () => go({ name: 'portableAgentInput', pluginId: plugin.pluginId }) },
1225
+ ];
1226
+ content = _jsx(ActionRows, { actions: actions, focus: focus });
1227
+ }
1228
+ else if (route.name === 'portableAgentInput' && plugin) {
1229
+ breadcrumb = `${plugin.displayName} / 通用 Skills / Agent`;
1230
+ context = '指定';
1231
+ title = '输入 Agent ID';
1232
+ description = inputError ?? '多个 Agent 使用空格分隔。';
1233
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { children: _jsx(Text, { children: portableAgentInput || ' ' }) }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u793A\u4F8B\uFF1Acursor claude-code codex gemini-cli" })] });
1234
+ }
1235
+ else if (route.name === 'portableScope' && plugin && snapshot.portableDraft) {
1236
+ breadcrumb = `${plugin.displayName} / 通用 Skills / 范围`;
1237
+ title = '在哪里使用这些 Skills?';
1238
+ description = `已选择 ${snapshot.portableDraft.selectedCount} 个 Skills · Agent:${snapshot.portableDraft.agentMode === 'automatic' ? '自动选择' : snapshot.portableDraft.requestedAgents.join('、')}`;
1239
+ actions = [
1240
+ { label: '当前项目', description: '安装到当前项目的 Skill 目录', meta: '推荐', run: () => beginPortableReview(plugin.pluginId, 'project') },
1241
+ { label: '所有项目', description: '安装到用户级 Skill 目录', run: () => beginPortableReview(plugin.pluginId, 'user') },
1242
+ ];
1243
+ content = _jsx(ActionRows, { actions: actions, focus: focus });
1244
+ }
1245
+ else if (route.name === 'portableBlocker' && plugin) {
1246
+ breadcrumb = `${plugin.displayName} / 通用 Skills`;
1247
+ title = route.code === 'requiredConfigMissing' ? '无法准备通用 Skills'
1248
+ : route.code === 'portableScopeUnavailable' ? '所选范围不可用'
1249
+ : route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible' ? '暂时无法安装通用 Skills' : '无法准备通用 Skills';
1250
+ description = route.code === 'requiredConfigMissing' ? '插件缺少生成内容所需的设置。'
1251
+ : route.code === 'portableScopeUnavailable' ? '至少一个所选 Skill 不适用于这个范围。请返回 Skill 选择或更改范围。'
1252
+ : route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible' ? '没有找到兼容的 Skills CLI。' : '候选 Skill 没有通过 Portable 校验。';
1253
+ actions = [
1254
+ ...(route.code === 'requiredConfigMissing' ? [{ label: '完成插件设置', run: () => {
1255
+ props.controller.replace({ name: 'configLayer', pluginId: plugin.pluginId });
1256
+ setFocus(0);
1257
+ setScrollOffset(0);
1258
+ } }] : []),
1259
+ ...(route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible'
1260
+ ? [{ label: '查看安装说明', run: () => setShowTechnical((value) => !value) }] : []),
1261
+ ...(route.code === 'portableScopeUnavailable' ? [{ label: '返回 Skill 选择', run: () => {
1262
+ props.controller.resetPortableFlowToPreview(plugin.pluginId, { name: 'portableSkillSelect', pluginId: plugin.pluginId }, true);
1263
+ setFocus(0);
1264
+ setScrollOffset(0);
1265
+ } }] : []),
1266
+ { label: '重新检查', run: () => beginPortable(plugin.pluginId) },
1267
+ { label: '返回插件预览', run: () => props.controller.resetPortableFlowToPreview(plugin.pluginId) },
1268
+ ];
1269
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", route.code] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u6CA1\u6709\u8C03\u7528 Skills CLI \u5B89\u88C5\u3002" })] }), _jsx(ActionRows, { actions: actions, focus: focus }), showTechnical ? _jsx(Bordered, { title: route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible' ? '安装说明' : '技术详情', children: _jsx(Text, { color: tuiTheme.colors.muted, children: route.code === 'portableSkillsCliMissing' || route.code === 'portableSkillsCliIncompatible' ? '安装或升级兼容的 Skills CLI(>=1.5.23 <2.0.0),然后重新检查。' : `诊断代码:${route.diagnosticCode}` }) }) : _jsx(Text, { color: tuiTheme.colors.muted, children: "d \u67E5\u770B\u8131\u654F\u8BCA\u65AD" })] });
1270
+ }
1271
+ else if (route.name === 'pluginDependencies' && plugin) {
1272
+ const overview = dependencyOverview(plugin);
1273
+ const visibleDependents = route.view === 'removeBlocked' && route.platform && route.scope
1274
+ ? props.controller.getBlockingDependents(plugin.pluginId, route.platform, route.scope)
1275
+ : overview?.dependents ?? [];
1276
+ const relationActions = [
1277
+ ...(overview?.dependencies ?? []).map((entry) => {
1278
+ const state = dependencyStatus(entry);
1279
+ return {
1280
+ label: entry.displayName,
1281
+ description: dependencyRelationDescription(entry),
1282
+ meta: dependencyPlacementLabel(entry),
1283
+ status: state.status,
1284
+ run: () => go({ name: 'pluginDetail', pluginId: entry.pluginId }),
1285
+ };
1286
+ }),
1287
+ ...visibleDependents.map((entry) => {
1288
+ const state = dependencyStatus(entry);
1289
+ return {
1290
+ label: entry.displayName,
1291
+ description: dependencyRelationDescription(entry),
1292
+ meta: route.view === 'removeBlocked' ? dependentTargetLabel(entry) : dependencyPlacementLabel(entry),
1293
+ status: state.status,
1294
+ run: () => go({ name: 'pluginDetail', pluginId: entry.pluginId }),
1295
+ };
1296
+ }),
1297
+ ];
1298
+ const dependencyCount = overview?.dependencies.length ?? 0;
1299
+ const dependentCount = visibleDependents.length;
1300
+ if (route.view === 'removeBlocked') {
1301
+ breadcrumb = `${plugin.displayName} / 移除`;
1302
+ context = '已阻止';
1303
+ title = `无法移除 ${plugin.displayName}`;
1304
+ description = '仍有插件依赖它。请先处理依赖者,再重新尝试。';
1305
+ actions = [
1306
+ ...relationActions.slice(dependencyCount),
1307
+ ...(route.platform && route.scope ? [{
1308
+ label: '强制清理…',
1309
+ description: '依赖者会变为不完整;下一页仍需明确确认',
1310
+ status: 'error',
1311
+ run: () => beginReview({
1312
+ kind: 'pluginWrite',
1313
+ action: 'cleanup',
1314
+ pluginId: plugin.pluginId,
1315
+ platform: route.platform,
1316
+ scope: route.scope,
1317
+ force: true,
1318
+ }),
1319
+ }] : []),
1320
+ { label: '查看完整依赖关系', run: () => {
1321
+ props.controller.replace({ name: 'pluginDependencies', pluginId: plugin.pluginId });
1322
+ setFocus(0);
1323
+ setScrollOffset(0);
1324
+ } },
1325
+ { label: '返回插件详情', run: back },
1326
+ ];
1327
+ const consumerActions = actions.slice(0, dependentCount);
1328
+ const blockedViewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
1329
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", dependentCount, " \u4E2A\u5DF2\u5B89\u88C5\u63D2\u4EF6\u4ECD\u5728\u4F7F\u7528"] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u6CA1\u6709\u4FEE\u6539\u4EFB\u4F55\u5B89\u88C5\u3002" })] }), _jsx(Bordered, { title: "\u4F7F\u7528\u8FD9\u4E2A\u4F9D\u8D56\u7684\u63D2\u4EF6", children: _jsx(ActionRows, { actions: consumerActions, focus: focus, offset: blockedViewport.offset, limit: blockedViewport.limit }) }), _jsx(ActionRows, { actions: actions.slice(dependentCount), focus: focus - dependentCount })] });
1330
+ }
1331
+ else {
1332
+ breadcrumb = `${plugin.displayName} / 依赖关系`;
1333
+ context = overview ? `${overview.summary.totalCount + 1} 个插件` : '只读';
1334
+ title = hasInstalledPlacement(plugin) ? '依赖与被使用情况' : '安装所需插件';
1335
+ description = '依赖从同一个来源快照解析;关系页不会修改安装。';
1336
+ actions = [
1337
+ ...relationActions,
1338
+ { label: '技术详情', description: '显示 canonical pluginId 和安全错误代码', run: () => setShowTechnical((value) => !value) },
1339
+ ];
1340
+ const viewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
1341
+ const visibleRows = (start, end) => relationActions
1342
+ .map((_, index) => index)
1343
+ .filter((index) => index >= start && index < end
1344
+ && index >= viewport.offset
1345
+ && (viewport.limit === undefined || index < viewport.offset + viewport.limit))
1346
+ .map((index) => _jsx(ActionRows, { actions: actions, focus: focus, offset: index, limit: 1 }, index));
1347
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { title: "\u4F60\u9009\u62E9\u7684\u63D2\u4EF6", children: _jsxs(Text, { children: [plugin.displayName, " ", _jsx(Text, { color: tuiTheme.colors.muted, children: statusText(plugin) })] }) }), dependencyCount > 0 ? _jsx(Bordered, { title: "\u5FC5\u9700\u4F9D\u8D56", children: visibleRows(0, dependencyCount) }) : null, dependentCount > 0 ? _jsx(Bordered, { title: "\u4F7F\u7528\u5B83\u7684\u63D2\u4EF6", children: visibleRows(dependencyCount, dependencyCount + dependentCount) }) : null, overview?.blockers.map((blocker) => _jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", blocker.title] }), _jsx(Text, { color: tuiTheme.colors.muted, children: blocker.explanation })] }, blocker.code)), relationActions.length === 0 ? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u6CA1\u6709\u53EF\u663E\u793A\u7684\u4F9D\u8D56\u5173\u7CFB\u3002" }) : null, viewport.limit !== undefined && viewport.offset + viewport.limit > relationActions.length
1348
+ ? _jsx(ActionRows, { actions: actions, focus: focus, offset: relationActions.length, limit: 1 })
1349
+ : null, showTechnical ? _jsxs(Bordered, { title: "\u6280\u672F\u8BE6\u60C5", children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["canonical pluginId ", plugin.pluginId] }), overview?.dependencies.map((entry) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [entry.pluginId, " \u00B7 ", dependencyRelationDescription(entry)] }, entry.pluginId))] }) : null] });
1350
+ }
1351
+ }
1352
+ else if (route.name === 'installPlacement' && plugin) {
1353
+ breadcrumb = `${plugin.displayName} / 添加`;
1354
+ context = '第 1 步,共 2 步';
1355
+ title = `你希望在哪里使用 ${plugin.displayName}?`;
1356
+ const overview = dependencyOverview(plugin);
1357
+ const hasDependencies = (plugin.dependencySummary?.totalCount ?? 0) > 0;
1358
+ description = hasDependencies
1359
+ ? '根插件和缺失依赖使用同一个位置;健康的所有项目依赖可在当前项目复用。'
1360
+ : '工具已根据插件能力自动选择;需要时可以调整。';
1361
+ const availableScopes = ['user', 'project'].filter((candidateScope) => selectedPlatforms(plugin, candidateScope).some((entry) => !entry.disabled));
1362
+ const reusableDependencies = overview?.dependencies.filter((dependency) => dependency.placements.some((placement) => placement.state === 'installed' && placement.health === 'ready'
1363
+ && (scope === 'project' || placement.scope === 'user'))).length ?? 0;
1364
+ const missingDependencies = Math.max(0, (overview?.summary.totalCount ?? 0) - reusableDependencies);
1365
+ actions = [
1366
+ ...availableScopes.map((candidateScope) => ({
1367
+ label: `${candidateScope === scope ? '●' : '○'} ${scopeLabel(candidateScope)}`,
1368
+ description: candidateScope === 'user'
1369
+ ? hasDependencies ? '根插件和缺失依赖都可在任意项目中使用' : '在任意项目中可使用,适合个人常用能力'
1370
+ : hasDependencies ? '缺失依赖添加到当前项目;已有所有项目依赖会复用' : '仅在当前项目中使用,适合团队共享',
1371
+ meta: candidateScope === 'user' ? '推荐' : undefined,
1372
+ run: () => {
1373
+ setScope(candidateScope);
1374
+ setTools(selectedPlatforms(plugin, candidateScope).filter((entry) => !entry.disabled).map((entry) => entry.platform));
1375
+ },
1376
+ })),
1377
+ { label: '继续', disabled: tools.length === 0 ? '没有可安装的工具' : undefined, run: async () => beginReview(installIntent(plugin)) },
1378
+ { label: '调整工具…', run: () => {
1379
+ const choices = selectedPlatforms(plugin, scope);
1380
+ go({ name: 'installTools', pluginId: plugin.pluginId,
1381
+ ...(choices.every((choice) => choice.disabled) ? { view: 'blocked' } : {}) });
1382
+ } },
1383
+ ];
1384
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(ActionRows, { actions: actions.slice(0, availableScopes.length), focus: focus }), hasDependencies ? _jsxs(Bordered, { title: "\u4F9D\u8D56\u5904\u7406", children: [_jsxs(Text, { color: reusableDependencies > 0 ? tuiTheme.statusColors.success : tuiTheme.colors.muted, children: ["\u2713 ", reusableDependencies, " \u4E2A\u4F9D\u8D56\u4F1A\u590D\u7528\u73B0\u6709\u5B89\u88C5"] }), _jsxs(Text, { color: missingDependencies > 0 ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: ["+ ", missingDependencies, " \u4E2A\u4F9D\u8D56\u5C06\u5B89\u88C5\u5230\u6240\u9009\u4F4D\u7F6E"] })] }) : null, _jsx(Bordered, { title: "\u53EF\u7528\u5DE5\u5177", children: _jsxs(Text, { children: [tools.map(platformLabel).join('、') || '当前没有可用工具', "\uFF08\u5DF2\u81EA\u52A8\u9009\u62E9\uFF09"] }) }), _jsx(ActionRows, { actions: actions.slice(availableScopes.length), focus: focus - availableScopes.length })] });
1385
+ }
1386
+ else if (route.name === 'installTools' && plugin) {
1387
+ breadcrumb = `${plugin.displayName} / 添加 / 工具`;
1388
+ context = '可选';
1389
+ title = '选择要添加到哪些工具';
1390
+ const hasDependencies = (plugin.dependencySummary?.totalCount ?? 0) > 0;
1391
+ description = hasDependencies
1392
+ ? '只有根插件和所有依赖都支持所选位置的工具才可选择。'
1393
+ : '同一插件、同一平台只能安装到一个位置。';
1394
+ const choices = selectedPlatforms(plugin, scope);
1395
+ const allUnavailable = choices.length > 0 && choices.every((choice) => choice.disabled);
1396
+ if (route.view === 'blocked' || allUnavailable) {
1397
+ context = '依赖限制';
1398
+ title = '无法添加到所选工具';
1399
+ description = '根插件或必需依赖不支持当前工具组合,因此不会进入确认页。';
1400
+ actions = [
1401
+ { label: '查看依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }) },
1402
+ { label: '返回调整位置', run: back },
1403
+ ];
1404
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { children: choices.map((choice) => _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u25C7 ", platformLabel(choice.platform), " \u4E0D\u53EF\u9009", choice.disabled ? ` · ${choice.disabled}` : ''] }, choice.platform)) }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1405
+ }
1406
+ else {
1407
+ actions = choices.map((choice) => ({
1408
+ label: `${tools.includes(choice.platform) ? '◆' : '◇'} ${platformLabel(choice.platform)}`,
1409
+ description: choice.disabled ?? '当前未安装,支持所选位置',
1410
+ meta: tools.includes(choice.platform) ? '已选择' : undefined,
1411
+ disabled: choice.disabled,
1412
+ run: () => {
1413
+ setDirty(true);
1414
+ setTools((current) => current.includes(choice.platform)
1415
+ ? current.filter((platform) => platform !== choice.platform)
1416
+ : [...current, choice.platform]);
1417
+ },
1418
+ }));
1419
+ help = '↑↓ 移动 · Space 切换 · Enter 完成 · Esc 返回';
1420
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(ActionRows, { actions: actions, focus: focus }), hasDependencies ? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u4E0D\u517C\u5BB9\u7684\u5DE5\u5177\u4E0D\u80FD\u9009\u62E9\uFF1B\u8BF7\u67E5\u770B\u4F9D\u8D56\u5173\u7CFB\u4E86\u89E3\u539F\u56E0\u3002" }) : null, _jsxs(Text, { color: tools.length > 0 ? tuiTheme.colors.accent : tuiTheme.statusColors.warning, children: ["Enter ", tools.length > 0 ? '完成选择' : '至少选择一个工具'] })] });
1421
+ }
1422
+ }
1423
+ else if (route.name === 'pluginDetail' && plugin) {
1424
+ breadcrumb = `我的插件 / ${plugin.displayName}`;
1425
+ context = `${plugin.sourceLabel}${plugin.version ? ` · ${plugin.version}` : ''}`;
1426
+ title = plugin.displayName;
1427
+ description = plugin.description;
1428
+ const dependencySummary = plugin.dependencySummary;
1429
+ const placementActions = plugin.installedPlacements.map((placement) => {
1430
+ const projected = placementStatus(placement);
1431
+ return {
1432
+ label: platformLabel(placement.platform),
1433
+ description: projected.description,
1434
+ meta: projected.value,
1435
+ status: projected.status,
1436
+ run: () => placement.state === 'scopeConflict'
1437
+ ? go({ name: 'issueDetail', issueId: `scope:${plugin.pluginId}:${placement.platform}` })
1438
+ : go({ name: 'platform', pluginId: plugin.pluginId, platform: placement.platform }),
1439
+ };
1440
+ });
1441
+ const dependencyActions = dependencySummary ? [{
1442
+ label: '必需依赖',
1443
+ description: dependencySummary.issueCount > 0 ? `${dependencySummary.issueCount} 项需要处理` : '查看直接、间接依赖和被使用情况',
1444
+ meta: dependencySummaryText(dependencySummary),
1445
+ status: dependencySummary.issueCount > 0 ? 'warning' : undefined,
1446
+ run: () => go({ name: 'pluginDependencies', pluginId: plugin.pluginId }),
1447
+ }] : [];
1448
+ const portableActions = plugin.portableSkills ? [{
1449
+ label: '通用 Skills',
1450
+ description: '由插件提供;安装状态未知',
1451
+ meta: '已提供',
1452
+ run: () => beginPortable(plugin.pluginId),
1453
+ }] : [];
1454
+ actions = [
1455
+ ...placementActions,
1456
+ ...dependencyActions,
1457
+ ...portableActions,
1458
+ { label: '设置', description: '个人默认、当前项目和本机覆盖', meta: `${plugin.configFieldCount} 项配置`, run: () => go({ name: 'configLayer', pluginId: plugin.pluginId }) },
1459
+ { label: '关于与技术详情', description: '版本、来源和受管内容', run: () => go({ name: 'pluginAbout', pluginId: plugin.pluginId }) },
1460
+ ];
1461
+ const dependencyOffset = placementActions.length;
1462
+ const portableOffset = dependencyOffset + dependencyActions.length;
1463
+ const settingOffset = portableOffset + portableActions.length;
1464
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { title: "\u4F7F\u7528\u72B6\u6001", children: _jsx(ActionRows, { actions: placementActions, focus: focus }) }), dependencySummary ? _jsx(Bordered, { title: "\u4F9D\u8D56\u5173\u7CFB", children: _jsx(ActionRows, { actions: dependencyActions, focus: focus - dependencyOffset }) }) : null, plugin.portableSkills ? _jsx(Bordered, { title: "\u901A\u7528 Skills", children: _jsx(ActionRows, { actions: portableActions, focus: focus - portableOffset }) }) : null, _jsx(Bordered, { title: "\u63D2\u4EF6\u8BBE\u7F6E", children: _jsx(ActionRows, { actions: actions.slice(settingOffset, -1), focus: focus - settingOffset }) }), _jsx(ActionRows, { actions: actions.slice(-1), focus: focus - actions.length + 1 })] });
1465
+ }
1466
+ else if (route.name === 'platform' && plugin) {
1467
+ const placement = props.controller.getPlatformPlacement(plugin.pluginId, route.platform);
1468
+ breadcrumb = `${plugin.displayName} / ${platformLabel(route.platform)}`;
1469
+ if (plugin.aggregateStatus === 'unavailable') {
1470
+ title = `暂时无法管理 ${platformLabel(route.platform)}`;
1471
+ description = '插件来源不可用,当前状态不能安全确认。';
1472
+ actions = [{ label: '查看来源问题', description: `${plugin.sourceId} 无可用缓存`, run: () => go({ name: 'issueDetail', issueId: `source:${plugin.sourceId}:unavailable` }) }, { label: '重新检查状态', description: '不会修改现场内容', run: () => props.controller.refreshSource(plugin.sourceId) }];
1473
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.statusColors.error, children: "\u00D7 \u6765\u6E90\u4E0D\u53EF\u7528" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u63D2\u4EF6\u8BBE\u7F6E\u4FDD\u6301\u4E0D\u53D8" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1474
+ }
1475
+ else if (!placement || placement.state === 'notInstalled') {
1476
+ title = `尚未添加到 ${platformLabel(route.platform)}`;
1477
+ description = '此插件支持所列使用位置。';
1478
+ actions = [{ label: `添加到 ${platformLabel(route.platform)}…`, description: '选择使用位置', run: () => {
1479
+ const initialScope = placement?.state === 'notInstalled'
1480
+ ? placement.supportedScopes[0] ?? scope
1481
+ : scope;
1482
+ setScope(initialScope);
1483
+ setTools([route.platform]);
1484
+ go({ name: 'installPlacement', pluginId: plugin.pluginId });
1485
+ } }, { label: '查看支持信息', description: 'manifest 与平台能力', run: () => go({ name: 'pluginAbout', pluginId: plugin.pluginId }) }];
1486
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.colors.muted, children: "\u25CB \u672A\u6DFB\u52A0" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u63D2\u4EF6\u8BBE\u7F6E\u4FDD\u6301\u4E0D\u53D8" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1487
+ }
1488
+ else if (placement.state === 'scopeConflict') {
1489
+ title = '安装位置冲突';
1490
+ description = '正常状态只能保留一个位置。';
1491
+ actions = [{ label: '查看位置冲突问题', run: () => go({ name: 'issueDetail', issueId: `scope:${plugin.pluginId}:${route.platform}` }) }];
1492
+ content = _jsx(ActionRows, { actions: actions, focus: focus });
1493
+ }
1494
+ else if (placement.health === 'error') {
1495
+ const typedIssue = props.controller.listUserIssues().find((candidate) => candidate.pluginId === plugin.pluginId
1496
+ && candidate.platform === route.platform
1497
+ && candidate.scope === placement.scope);
1498
+ title = `${platformLabel(route.platform)} 暂时不可管理`;
1499
+ description = typedIssue?.explanation ?? '平台状态检查失败,当前不能安全选择写操作。';
1500
+ actions = [
1501
+ ...(typedIssue ? [{ label: '查看对应问题', description: typedIssue.title, run: () => go({ name: 'issueDetail', issueId: typedIssue.issueId }) }] : []),
1502
+ { label: '查看安全技术详情', description: '只显示类型化状态和错误码', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
1503
+ { label: '返回插件', run: back },
1504
+ ];
1505
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.statusColors.error, children: "\u00D7 \u72B6\u6001\u68C0\u67E5\u5931\u8D25" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5F53\u524D\u53EA\u63D0\u4F9B\u53EA\u8BFB\u64CD\u4F5C" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1506
+ }
1507
+ else if (placement.health === 'incomplete') {
1508
+ title = `${platformLabel(route.platform)} ${placement.health === 'incomplete' ? '安装不完整' : '暂时不可用'}`;
1509
+ description = '缺少一项必要内容,插件可能无法稳定启动。';
1510
+ actions = [
1511
+ { label: '重新连接缺失内容…', description: '保留现有设置', run: () => beginReview({ kind: 'pluginWrite', action: 'reinstall', pluginId: plugin.pluginId, platform: route.platform, scope: placement.scope }) },
1512
+ { label: '查看影响', description: '缺少的内容和范围', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
1513
+ { label: `从${scopeLabel(placement.scope)}移除…`, description: '保留插件设置', status: 'error', run: () => beginPluginRemoval(plugin, route.platform, placement.scope) },
1514
+ ];
1515
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.statusColors.warning, children: "! \u9700\u8981\u5904\u7406" }), _jsxs(Text, { color: tuiTheme.colors.muted, children: [scopeLabel(placement.scope), " \u00B7 ", plugin.sourceLabel] })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1516
+ }
1517
+ else {
1518
+ title = `已安装到${scopeLabel(placement.scope)}`;
1519
+ description = `当前检查正常${plugin.version ? `;版本 ${plugin.version}` : ''}。`;
1520
+ actions = [
1521
+ { label: '运行升级…', description: '使用当前来源中的版本重建', run: () => beginReview({ kind: 'pluginWrite', action: 'upgrade', pluginId: plugin.pluginId, platform: route.platform, scope: placement.scope }) },
1522
+ { label: '重新安装…', description: '重新生成并验证受管内容', run: () => beginReview({ kind: 'pluginWrite', action: 'reinstall', pluginId: plugin.pluginId, platform: route.platform, scope: placement.scope }) },
1523
+ { label: `从${scopeLabel(placement.scope)}移除…`, description: '保留插件设置', status: 'error', run: () => beginPluginRemoval(plugin, route.platform, placement.scope) },
1524
+ { label: '查看安装详情', description: '组件与诊断信息', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) },
1525
+ ];
1526
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.statusColors.success, children: "\u2713 \u72B6\u6001\u6B63\u5E38" }), _jsxs(Text, { color: tuiTheme.colors.muted, children: [scopeLabel(placement.scope), plugin.version ? ` · 当前版本 ${plugin.version}` : ''] })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1527
+ }
1528
+ }
1529
+ else if (route.name === 'configLayer' && plugin) {
1530
+ breadcrumb = `${plugin.displayName} / 设置`;
1531
+ title = '选择要查看或修改的设置';
1532
+ description = '安装位置与设置保存层是两个不同概念。';
1533
+ help = '↑↓ 移动 · Enter 打开 · Esc 返回';
1534
+ actions = [
1535
+ { label: '有效设置', description: '合并后的实际结果 · 只读', meta: '查看', run: () => go({ name: 'configFields', pluginId: plugin.pluginId, layer: { scope: 'effective', projectLayer: 'shared' } }) },
1536
+ { label: '个人默认', description: '在所有项目中作为默认值', meta: configLayerCounts.user === undefined ? undefined : `${configLayerCounts.user} 个值`, run: () => go({ name: 'configFields', pluginId: plugin.pluginId, layer: { scope: 'user', projectLayer: 'shared' } }) },
1537
+ { label: '当前项目', description: '随仓库共享,适合团队约定', meta: configLayerCounts.project === undefined ? undefined : `${configLayerCounts.project} 个值`, run: () => go({ name: 'configFields', pluginId: plugin.pluginId, layer: { scope: 'project', projectLayer: 'shared' } }) },
1538
+ { label: '当前项目(仅本机)', description: '只保存在本机,不提交到仓库', meta: configLayerCounts.local === undefined ? undefined : configLayerCounts.local === 0 ? '未设置' : `${configLayerCounts.local} 个值`, run: () => go({ name: 'configFields', pluginId: plugin.pluginId, layer: { scope: 'project', projectLayer: 'local' } }) },
1539
+ ];
1540
+ content = _jsx(ActionRows, { actions: actions, focus: focus });
1541
+ }
1542
+ else if (route.name === 'configFields' && plugin) {
1543
+ const readonly = route.layer.scope === 'effective';
1544
+ const selectedGroups = route.groupId
1545
+ ? currentConfigPage?.groups.filter((group) => group.id === route.groupId) ?? []
1546
+ : currentConfigPage?.groups ?? [];
1547
+ breadcrumb = `${plugin.displayName} / 设置 / ${configLayerLabel(route.layer)}`;
1548
+ context = readonly ? '只读' : dirty ? `${Object.keys(draftUpdates).length + draftRemoves.length} 项未保存` : undefined;
1549
+ title = readonly ? '当前实际使用的设置' : route.groupId ? selectedGroups[0]?.title ?? '高级设置' : configLayerLabel(route.layer);
1550
+ description = readonly ? '由默认值、个人默认、当前项目和本机覆盖合并。' : route.groupId ? '这些字段来自 manifest 的高级设置分组。' : '字段来自插件 manifest;修改先保存在本地草稿。';
1551
+ if (!readonly && configActionError)
1552
+ description = configActionError;
1553
+ help = readonly ? '↑↓ 查看 · Esc 返回' : '↑↓ 移动 · Enter 修改 · s 保存 · u 恢复继承 · Esc 返回';
1554
+ if (!currentConfigPage) {
1555
+ content = _jsx(Text, { color: tuiTheme.colors.accent, children: "\u25CF \u6B63\u5728\u8BFB\u53D6\u53EF\u4FE1\u8BBE\u7F6E\u5B9A\u4E49" });
1556
+ }
1557
+ else if (readonly) {
1558
+ actions = selectedGroups.flatMap((group) => group.fields.map((field) => ({
1559
+ label: field.title,
1560
+ description: `来自${field.sourceLabel}`,
1561
+ meta: currentFieldDisplay(field),
1562
+ })));
1563
+ const limit = policy.mode === 'compact' ? 4 : 7;
1564
+ content = _jsx(Bordered, { title: "\u6709\u6548\u8BBE\u7F6E", children: _jsx(ActionRows, { actions: actions, focus: focus, offset: scrollOffset, limit: limit }) });
1565
+ }
1566
+ else {
1567
+ const mutableLayer = {
1568
+ scope: route.layer.scope,
1569
+ projectLayer: route.layer.projectLayer,
1570
+ };
1571
+ const rowGroups = [];
1572
+ if (!route.groupId && currentConfigPage.presets.length > 0) {
1573
+ rowGroups.push({ id: 'presets', title: '快速方案', rows: [{ label: '当前方案', description: '应用 manifest 声明的预设', meta: `${currentConfigPage.presets.length} 个`, run: () => go({ name: 'configPresets', pluginId: plugin.pluginId, layer: mutableLayer }) }] });
1574
+ }
1575
+ for (const group of selectedGroups) {
1576
+ const rows = group.advanced && !route.groupId
1577
+ ? [{ label: group.title, description: group.description ?? '渐进展开高级字段', meta: `${group.fields.length} 项`, run: () => go({ name: 'configFields', pluginId: plugin.pluginId, layer: route.layer, groupId: group.id }) }]
1578
+ : group.fields.map((field) => ({
1579
+ label: field.title,
1580
+ description: field.description,
1581
+ meta: currentFieldDisplay(field),
1582
+ configKey: field.key,
1583
+ run: () => go({ name: 'configFieldEditor', pluginId: plugin.pluginId, layer: mutableLayer, fieldKey: field.key, editor: fieldEditor(field) }),
1584
+ }));
1585
+ rowGroups.push({ id: group.id, title: group.advanced && !route.groupId ? '高级设置' : group.title, rows });
1586
+ }
1587
+ actions = rowGroups.flatMap((group) => group.rows);
1588
+ if (!route.groupId)
1589
+ actions.push({ label: '保存修改', description: dirty ? '查看安全变更汇总' : '当前没有未保存修改', disabled: dirty ? undefined : '当前没有未保存修改', run: beginConfigSave }, {
1590
+ label: '重置此设置层…',
1591
+ description: '删除本层全部值并恢复继承',
1592
+ status: 'error',
1593
+ disabled: currentConfigPage.storedCount === 0 ? '本层没有已存值' : undefined,
1594
+ run: () => beginReview({ kind: 'configReset', pluginId: plugin.pluginId, scope: route.layer.scope, projectLayer: route.layer.projectLayer }),
1595
+ });
1596
+ let rowOffset = 0;
1597
+ content = policy.mode === 'compact' || scrollOffset > 0
1598
+ ? _jsx(Bordered, { title: "\u8BBE\u7F6E\u5B57\u6BB5", children: _jsx(ActionRows, { actions: actions, focus: focus, offset: scrollOffset, limit: policy.mode === 'compact' ? 4 : 7 }) })
1599
+ : _jsxs(Box, { flexDirection: "column", children: [rowGroups.map((group) => {
1600
+ const offset = rowOffset;
1601
+ rowOffset += group.rows.length;
1602
+ return _jsx(Bordered, { title: group.title, children: _jsx(ActionRows, { actions: actions, focus: focus, offset: offset, limit: group.rows.length }) }, group.id);
1603
+ }), !route.groupId ? _jsx(ActionRows, { actions: actions, focus: focus, offset: rowOffset }) : null] });
1604
+ }
1605
+ }
1606
+ else if (route.name === 'configFieldEditor' && plugin && activeField) {
1607
+ breadcrumb = `${plugin.displayName} / 设置 / ${activeField.title}`;
1608
+ help = route.editor === 'enum' || route.editor === 'boolean'
1609
+ ? '↑↓ 移动 · Enter 使用 · Esc 取消'
1610
+ : '输入 · Enter 继续 · Ctrl-U 删除本层值 · Esc 取消';
1611
+ if (route.editor === 'enum') {
1612
+ title = `选择${activeField.title}`;
1613
+ description = '保存值仍是 manifest 中的原始 enum token。';
1614
+ actions = (activeField.enumValues ?? []).map((value) => ({
1615
+ label: `${activeField.valueLabels?.[value] ?? value}${activeField.recommendedValue === value ? '(推荐)' : ''}`,
1616
+ meta: draftUpdates[activeField.key] === value || (!Object.hasOwn(draftUpdates, activeField.key) && activeField.value === value) ? '●' : '○',
1617
+ run: () => { updateDraft(activeField.key, value); back(); },
1618
+ }));
1619
+ content = _jsx(ActionRows, { actions: actions, focus: focus });
1620
+ }
1621
+ else if (route.editor === 'boolean') {
1622
+ title = `${activeField.title}?`;
1623
+ description = activeField.description ?? '选择开启或关闭。';
1624
+ actions = [{ label: '开启', run: () => { updateDraft(activeField.key, true); back(); } }, { label: '关闭', run: () => { updateDraft(activeField.key, false); back(); } }];
1625
+ content = _jsx(ActionRows, { actions: actions, focus: focus });
1626
+ }
1627
+ else {
1628
+ title = route.editor === 'number' ? `输入${activeField.title}` : route.editor === 'path' ? `选择${activeField.title}` : route.editor === 'sensitive' ? '输入敏感值' : `输入${activeField.title}`;
1629
+ description = route.editor === 'number'
1630
+ ? `允许范围:${activeField.min ?? '不限'}–${activeField.max ?? '不限'}${activeField.unit ? ` ${activeField.unit}` : ''}。`
1631
+ : route.editor === 'path' ? `${activeField.type === 'file' ? '文件' : '目录'}路径会在保存前由现有 schema 路径验证。`
1632
+ : route.editor === 'sensitive' ? '当前值不会回显;留空不会自动删除旧值。' : activeField.description ?? '';
1633
+ const visibleInput = route.editor === 'sensitive'
1634
+ ? '•'.repeat(inputValue.length)
1635
+ : route.editor === 'path' ? privatePathInput(inputValue) : inputValue;
1636
+ content = _jsxs(Bordered, { children: [_jsx(Text, { children: visibleInput || activeField.placeholder || ' ' }), _jsx(Text, { color: inputError ? tuiTheme.statusColors.error : tuiTheme.colors.muted, children: inputError ?? (route.editor === 'sensitive' ? '输入已隐藏。' : route.editor === 'path' ? '已隐藏父目录;Backspace 仍会编辑原始输入。' : activeField.placeholder ?? '按 Enter 使用输入值。') })] });
1637
+ }
1638
+ }
1639
+ else if (route.name === 'configPresets' && plugin) {
1640
+ breadcrumb = `${plugin.displayName} / 设置 / 预设`;
1641
+ title = '选择快速方案';
1642
+ description = '预设由插件 manifest 声明;应用后仍可逐项修改。';
1643
+ actions = (currentConfigPage?.presets ?? []).map((preset) => ({
1644
+ label: preset.title,
1645
+ description: (preset.privateFieldTitles?.length ?? 0) > 0
1646
+ ? `${preset.description ? `${preset.description} · ` : ''}${preset.privateFieldTitles.join('、')}需手动输入`
1647
+ : preset.description,
1648
+ run: () => {
1649
+ const fields = new Map(currentConfigPage?.groups.flatMap((group) => group.fields)
1650
+ .map((field) => [field.key, field]));
1651
+ const nextUpdates = { ...draftUpdates };
1652
+ for (const [key, value] of Object.entries(preset.values)) {
1653
+ const field = fields.get(key);
1654
+ if (field?.configured && field.value === value)
1655
+ delete nextUpdates[key];
1656
+ else
1657
+ nextUpdates[key] = value;
1658
+ }
1659
+ const nextRemoves = draftRemoves.filter((key) => !Object.hasOwn(preset.values, key));
1660
+ setDraftUpdates(nextUpdates);
1661
+ setDraftRemoves(nextRemoves);
1662
+ setDirty(Object.keys(nextUpdates).length + nextRemoves.length > 0);
1663
+ back();
1664
+ },
1665
+ }));
1666
+ content = currentConfigPage ? _jsx(ActionRows, { actions: actions, focus: focus }) : _jsx(Text, { color: tuiTheme.colors.accent, children: "\u25CF \u6B63\u5728\u8BFB\u53D6\u9884\u8BBE" });
1667
+ }
1668
+ else if (route.name === 'configUnavailable' && plugin) {
1669
+ breadcrumb = `${plugin.displayName} / 设置`;
1670
+ context = '暂时不可编辑';
1671
+ title = '暂时无法编辑设置';
1672
+ description = '当前无法取得可信的字段定义,因此不能验证新值。';
1673
+ actions = [
1674
+ { label: '重新检查来源', description: '刷新当前来源后重新读取可信定义', run: async () => {
1675
+ await props.controller.refreshSource(plugin.sourceId);
1676
+ setConfigLoadKey((value) => value + 1);
1677
+ props.controller.replace({ name: 'configFields', pluginId: plugin.pluginId, layer: route.layer ?? { scope: 'user', projectLayer: 'shared' } });
1678
+ } },
1679
+ ...(configResetAvailability?.sourceIdentityPresent
1680
+ ? [{ label: '技术详情', description: '查看可信身份和设置层摘要', run: () => setShowTechnical((value) => !value) }]
1681
+ : []),
1682
+ { label: '返回', run: back },
1683
+ ];
1684
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsx(Text, { color: tuiTheme.statusColors.error, children: "\u00D7 Manifest \u6216\u6765\u6E90\u4E0D\u53EF\u7528" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5DF2\u4FDD\u5B58\u7684\u8BBE\u7F6E\u4FDD\u6301\u4E0D\u53D8\u3002\u6062\u590D\u6765\u6E90\u540E\u624D\u80FD\u7F16\u8F91\u6216\u91CD\u7F6E\u3002" })] }), showTechnical && configResetAvailability?.sourceIdentityPresent ? _jsxs(Bordered, { title: "\u6280\u672F\u8BE6\u60C5", children: [_jsxs(Text, { children: ["\u6765\u6E90\u8EAB\u4EFD\uFF1A", configResetAvailability.sourceIdentity] }), _jsxs(Text, { children: ["\u8BBE\u7F6E\u6458\u8981\uFF1A", configResetAvailability.storeDigest] })] }) : null, _jsx(ActionRows, { actions: actions, focus: focus })] });
1685
+ }
1686
+ else if (route.name === 'sourceList') {
1687
+ breadcrumb = '插件来源';
1688
+ title = '插件来源';
1689
+ description = '单个来源失败不会阻止其他来源继续工作。';
1690
+ context = `${props.controller.listSources().length} 个来源`;
1691
+ help = '↑↓ 移动 · Enter 打开 · r 重新检查 · Esc 返回';
1692
+ actions = [
1693
+ ...props.controller.listSources().map((source) => {
1694
+ const overview = props.controller.getSourceOverview(source.id);
1695
+ const health = overview.health?.state ?? source.state;
1696
+ const status = health === 'ready' ? '已从远端加载'
1697
+ : health === 'path' ? '本地目录'
1698
+ : health === 'checking' ? '正在检查'
1699
+ : health === 'cache' || health === 'degraded' ? '使用缓存' : '无法读取';
1700
+ return {
1701
+ label: `${source.id}${source.primary ? ' 主来源' : ''}`,
1702
+ description: `${overview.detail?.type === 'path' ? '本地目录' : 'Git 仓库'} · ${source.count ?? 0} 个插件`,
1703
+ meta: status,
1704
+ status: health === 'unavailable' ? 'error'
1705
+ : health === 'cache' || health === 'degraded' ? 'warning'
1706
+ : source.primary ? 'success' : undefined,
1707
+ run: () => go({ name: 'sourceDetail', sourceId: source.id }),
1708
+ };
1709
+ }),
1710
+ { label: '+ 添加来源', description: 'Git 仓库或本地目录', run: beginAddSource },
1711
+ ];
1712
+ const viewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
1713
+ content = _jsx(ActionRows, { actions: actions, focus: focus, offset: viewport.offset, limit: viewport.limit });
1714
+ }
1715
+ else if (route.name === 'sourceDetail') {
1716
+ const overview = props.controller.getSourceOverview(route.sourceId);
1717
+ const detail = overview.detail;
1718
+ const state = overview.health?.state ?? overview.source?.state;
1719
+ breadcrumb = `插件来源 / ${route.sourceId}`;
1720
+ context = detail?.type === 'path' ? '本地目录' : 'Git';
1721
+ title = route.sourceId;
1722
+ description = state === 'cache' || state === 'degraded' ? '当前使用已缓存的 Catalog 数据。'
1723
+ : state === 'unavailable' ? '当前无法读取这个来源。' : '来源状态可用。';
1724
+ help = '↑↓ 移动 · Enter 选择 · r 重新检查 · Esc 返回';
1725
+ actions = [
1726
+ { label: '重新检查来源', description: '读取远端并更新缓存', run: () => props.controller.refreshSource(route.sourceId) },
1727
+ { label: '编辑来源…', description: 'ID 与类型创建后不可修改', run: () => go({ name: 'sourceEdit', sourceId: route.sourceId }) },
1728
+ { label: '设为主来源…', description: '只改变默认 marketplace', disabled: detail?.primary ? '当前已经是主来源' : undefined, run: () => beginReview({ kind: 'sourceMutation', action: 'setPrimary', sourceId: route.sourceId, changes: [{ title: '主来源', before: snapshot.viewModel?.sourceTabs.find((source) => source.primary)?.id, after: route.sourceId }] }) },
1729
+ { label: '移除来源…', description: '移除来源配置', status: 'error', disabled: detail?.primary ? '主来源不能直接移除' : undefined, run: () => beginReview({ kind: 'sourceMutation', action: 'remove', sourceId: route.sourceId, changes: [{ title: `移除来源 ${route.sourceId}` }] }) },
1730
+ { label: '技术详情', description: '只读身份与解析证据', run: () => go({ name: 'sourceTechnical', sourceId: route.sourceId }) },
1731
+ ];
1732
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { title: "\u5F53\u524D\u72B6\u6001", children: _jsx(Text, { color: state === 'unavailable' ? tuiTheme.statusColors.error : state === 'cache' || state === 'degraded' ? tuiTheme.statusColors.warning : tuiTheme.statusColors.success, children: state === 'checking' ? '● 正在检查' : state === 'unavailable' ? '× 无法读取' : state === 'cache' || state === 'degraded' ? '◐ 使用缓存' : '✓ 可用' }) }), _jsxs(Bordered, { title: "\u6765\u6E90\u914D\u7F6E", children: [_jsxs(Text, { children: ["\u4F4D\u7F6E ", detail?.locationSummary ?? '不可用'] }), _jsxs(Text, { children: ["\u7248\u672C\u9009\u62E9 ", detail?.type === 'path' ? '本地目录' : detail?.refPolicy === 'explicit' ? '固定 Git 引用' : '跟随 AW 版本通道'] }), detail?.explicitRef ? _jsxs(Text, { children: ["\u5F15\u7528 ", detail.explicitRef] }) : null, _jsxs(Text, { children: ["\u63D2\u4EF6 ", detail?.pluginCount ?? 0, " \u4E2A"] })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1733
+ }
1734
+ else if (route.name === 'sourceTechnical') {
1735
+ const detail = props.controller.getSourceOverview(route.sourceId).detail;
1736
+ breadcrumb = `插件来源 / ${route.sourceId} / 技术详情`;
1737
+ context = '只读';
1738
+ title = '来源身份与解析详情';
1739
+ description = '用于诊断和安全审查;不显示认证信息或绝对路径。';
1740
+ content = _jsxs(Bordered, { children: [_jsxs(Text, { children: ["sourceId ", route.sourceId] }), _jsxs(Text, { children: ["sourceIdentity ", detail?.sourceIdentity ?? '不可用'] }), _jsxs(Text, { children: ["requestedRef ", detail?.requestedRef ?? '不可用'] }), detail?.resolvedCommit ? _jsxs(Text, { children: ["resolvedCommit ", detail.resolvedCommit] }) : null, _jsxs(Text, { children: ["freshness ", detail?.freshness ?? '不可用'] }), _jsxs(Text, { children: ["configDigest ", detail?.configDigest ?? '不可用'] })] });
1741
+ }
1742
+ else if (route.name === 'sourceAdd' && sourceDraft?.owner === route.owner) {
1743
+ breadcrumb = sourceDraft.mode === 'add' ? '插件来源 / 添加' : `插件来源 / ${sourceDraft.sourceId} / 编辑`;
1744
+ const stepLabel = { type: '第 1 步,共 6 步', id: '第 2 步,共 6 步', location: '第 3 步,共 6 步', refPolicy: '第 4 步,共 6 步', ref: '第 5 步,共 6 步', subdir: '可选高级步骤' }[route.step];
1745
+ context = stepLabel;
1746
+ help = route.step === 'type' || route.step === 'refPolicy' ? '↑↓ 移动 · Enter 继续 · Esc 返回' : '输入 · Enter 继续 · Esc 返回';
1747
+ if (route.step === 'type') {
1748
+ title = '选择来源类型';
1749
+ description = '来源类型创建后不能修改。';
1750
+ actions = [
1751
+ { label: 'Git 仓库', description: '从远端 Git 仓库读取 Catalog', run: () => { setSourceDraft({ ...sourceDraft, type: 'git' }); go({ name: 'sourceAdd', step: sourceDraft.sourceId ? 'location' : 'id', owner: route.owner }); } },
1752
+ { label: '本地目录', description: '用于本地开发与测试', run: () => { setSourceDraft({ ...sourceDraft, type: 'path' }); go({ name: 'sourceAdd', step: sourceDraft.sourceId ? 'location' : 'id', owner: route.owner }); } },
1753
+ ];
1754
+ content = _jsx(ActionRows, { actions: actions, focus: focus });
1755
+ }
1756
+ else if (route.step === 'refPolicy') {
1757
+ title = '选择版本引用方式';
1758
+ description = '跟随 AW 通道适合常规来源;固定 ref 适合测试来源。';
1759
+ actions = [
1760
+ { label: '跟随 AW 版本通道', description: '自动选择 catalog/latest 或 catalog/beta', run: async () => { const next = { ...sourceDraft, refPolicy: 'cliChannel', ref: '' }; setSourceDraft(next); if (next.mode === 'update')
1761
+ await beginSourceUpdateReview(next);
1762
+ else
1763
+ go({ name: 'sourceAdd', step: 'subdir', owner: route.owner }); } },
1764
+ { label: '固定 Git 引用', description: 'branch、tag 或 commit', run: () => { setSourceDraft({ ...sourceDraft, refPolicy: 'explicit' }); go({ name: 'sourceAdd', step: 'ref', owner: route.owner }); } },
1765
+ ];
1766
+ content = _jsx(ActionRows, { actions: actions, focus: focus });
1767
+ }
1768
+ else {
1769
+ title = route.step === 'id' ? '输入来源 ID' : route.step === 'location' ? `输入 ${sourceDraft.type === 'git' ? 'Git URL' : '绝对路径'}` : route.step === 'ref' ? '输入固定 Git 引用' : '输入 Catalog 子目录';
1770
+ description = route.step === 'subdir' ? '仓库根目录是 Catalog 时保持为空。' : '输入会在继续前按来源规范验证。';
1771
+ const displayedInput = route.step === 'location' && sourceDraft.type === 'git' && sourceInput
1772
+ ? projectCatalogSourceLocation(sourceInput)
1773
+ : route.step === 'location' && sourceDraft.type === 'path'
1774
+ ? privatePathInput(sourceInput)
1775
+ : sourceInput;
1776
+ content = _jsxs(Bordered, { children: [_jsx(Text, { children: displayedInput || ' ' }), _jsx(Text, { color: sourceInputError ? tuiTheme.statusColors.error : tuiTheme.colors.muted, children: sourceInputError ?? (route.step === 'location' && sourceDraft.type === 'path' ? '已隐藏父目录;Backspace 仍会编辑原始输入。' : '按 Enter 继续。') })] });
1777
+ }
1778
+ }
1779
+ else if (route.name === 'sourceEdit') {
1780
+ const detail = props.controller.getSourceOverview(route.sourceId).detail;
1781
+ breadcrumb = `插件来源 / ${route.sourceId} / 编辑`;
1782
+ title = '选择要修改的内容';
1783
+ description = '来源 ID 和类型只读。';
1784
+ const startEdit = (field) => {
1785
+ if (!detail)
1786
+ return;
1787
+ const owner = `source-update:${route.sourceId}:${field}:${Date.now()}`;
1788
+ setSourceDraft({ owner, mode: 'update', sourceId: route.sourceId, type: detail.type, location: '', refPolicy: detail.refPolicy ?? 'cliChannel', ref: detail.explicitRef ?? '', subdir: detail.subdir ?? '', editField: field });
1789
+ setDirty(true);
1790
+ go({ name: 'sourceAdd', step: field === 'location' ? 'location' : field === 'subdir' ? 'subdir' : 'refPolicy', owner });
1791
+ };
1792
+ actions = detail?.type === 'git' ? [
1793
+ { label: 'Git URL', description: detail.locationSummary, run: () => startEdit('location') },
1794
+ { label: '版本引用', description: detail.refPolicy === 'explicit' ? `固定:${detail.explicitRef}` : '跟随 AW 版本通道', run: () => startEdit('refPolicy') },
1795
+ { label: 'Catalog 子目录', description: detail.subdir ?? '未设置', run: () => startEdit('subdir') },
1796
+ ] : [{ label: '本地目录', description: '已选择本地目录', run: () => startEdit('location') }];
1797
+ content = _jsx(ActionRows, { actions: actions, focus: focus });
1798
+ }
1799
+ else if (route.name === 'sourceRemoveBlocked') {
1800
+ breadcrumb = `插件来源 / ${route.sourceId} / 移除`;
1801
+ context = '已阻止';
1802
+ title = `无法移除 ${route.sourceId}`;
1803
+ description = sourceReferenceSummary?.inventoryComplete === false ? '引用清单不完整,不能把未知引用当作零。' : '仍有内容依赖这个来源。';
1804
+ actions = [{ label: sourceDependenciesExpanded ? '收起依赖内容' : '查看依赖内容', description: '只显示插件、来源、平台、位置和类别聚合', run: () => setSourceDependenciesExpanded((value) => !value) }, { label: '返回来源详情', run: back }];
1805
+ const countLabel = (value, unit) => value === undefined ? '未知' : `${value} ${unit}`;
1806
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { children: _jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", sourceReferenceSummary?.inventoryComplete === false ? '引用清单不完整' : '发现活动引用'] }) }), _jsxs(Bordered, { children: [_jsxs(Text, { children: ["\u5B89\u88C5\u4F4D\u7F6E ", countLabel(sourceReferenceSummary?.installationCount, '个')] }), _jsxs(Text, { children: ["\u63D2\u4EF6\u8BBE\u7F6E ", countLabel(sourceReferenceSummary?.configCount, '份')] }), _jsxs(Text, { children: ["\u751F\u547D\u5468\u671F\u72B6\u6001 ", countLabel(sourceReferenceSummary?.lifecycleCount, '个')] }), _jsxs(Text, { children: ["Marketplace ", countLabel(sourceReferenceSummary?.marketplaceCount, '个')] }), sourceDependenciesExpanded ? sourceReferenceSummary?.dependencies.slice(0, policy.mode === 'compact' ? 2 : 5).map((dependency, index) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [dependency.category, " \u00B7 ", dependency.pluginId ?? dependency.sourceId ?? '身份未知', dependency.platform ? ` · ${platformLabel(dependency.platform)}` : '', dependency.scope ? ` · ${scopeLabel(dependency.scope)}` : ''] }, `${dependency.category}:${dependency.pluginId ?? dependency.sourceId ?? index}`)) : null] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1807
+ }
1808
+ else if (route.name === 'issueList') {
1809
+ const issues = props.controller.listUserIssues();
1810
+ breadcrumb = '需要处理';
1811
+ context = issues.length > 0 ? `${issues.length} 项` : '没有问题';
1812
+ title = issues.length > 0 ? '需要处理' : '当前没有需要处理的问题';
1813
+ description = issues.length > 0
1814
+ ? '选择一个问题,查看影响和真实可用的处理路径。'
1815
+ : '当前状态检查没有发现需要处理的安装、来源或设置问题。';
1816
+ if (issues.length === 0) {
1817
+ actions = [
1818
+ { label: '返回首页', run: () => go({ name: 'home' }) },
1819
+ { label: '浏览插件', run: () => go({ name: 'pluginList', mode: 'browse' }) },
1820
+ ];
1821
+ }
1822
+ else {
1823
+ actions = issues.map((entry) => ({
1824
+ label: entry.category.startsWith('dependency')
1825
+ ? `${props.controller.getPluginOverview(entry.pluginId ?? '')?.displayName ?? entry.pluginId ?? entry.title}${entry.platform ? ` · ${platformLabel(entry.platform)}` : ''}`
1826
+ : entry.title,
1827
+ description: entry.category.startsWith('dependency')
1828
+ ? `${props.controller.getPluginOverview(entry.dependencyPluginId ?? '')?.displayName ?? entry.dependencyPluginId ?? entry.explanation} · ${entry.impact[0] ?? ''}`
1829
+ : `${entry.explanation} · ${entry.impact[0] ?? ''}`,
1830
+ status: entry.category === 'sourceDegraded' ? 'warning' : 'error',
1831
+ run: () => entry.category === 'dependencyStillRequired' && entry.pluginId
1832
+ ? go({ name: 'pluginDependencies', pluginId: entry.pluginId, view: 'removeBlocked',
1833
+ ...(entry.platform ? { platform: entry.platform } : {}),
1834
+ ...(entry.scope ? { scope: entry.scope } : {}) })
1835
+ : go({ name: 'issueDetail', issueId: entry.issueId }),
1836
+ }));
1837
+ }
1838
+ const viewport = actionViewport(route.name, actions, policy.mode, focus, scrollOffset);
1839
+ content = _jsx(ActionRows, { actions: actions, focus: focus, offset: viewport.offset, limit: viewport.limit });
1840
+ }
1841
+ else if (route.name === 'issueDetail' && issue) {
1842
+ breadcrumb = `需要处理 / ${issue.pluginId ?? issue.sourceId ?? issue.entity.id}`;
1843
+ context = issue.platform ? `${platformLabel(issue.platform)}${issue.scope ? ` · ${scopeLabel(issue.scope)}` : ''}` : issue.sourceId;
1844
+ title = issue.title;
1845
+ description = issue.explanation;
1846
+ const runIssueAction = async (entry) => {
1847
+ const intent = issueWriteIntent(issue, entry);
1848
+ if (intent) {
1849
+ await beginReview(intent);
1850
+ return;
1851
+ }
1852
+ if (entry.id === 'refreshSource' && issue.sourceId)
1853
+ await props.controller.refreshSource(issue.sourceId);
1854
+ else if (entry.id === 'continueWithCache' || entry.id === 'retryLater')
1855
+ go({ name: 'issueList' });
1856
+ else if (entry.id === 'editSource' && issue.sourceId)
1857
+ go({ name: 'sourceDetail', sourceId: issue.sourceId });
1858
+ else if (entry.id === 'reAddSource' && issue.sourceId)
1859
+ beginIssueSourceAdd(issue.sourceId);
1860
+ else if (entry.id === 'viewSourceChange' && issue.sourceId)
1861
+ go({ name: 'sourceTechnical', sourceId: issue.sourceId });
1862
+ else if (entry.id === 'openConfig' && issue.pluginId) {
1863
+ go(issue.configLayer
1864
+ ? { name: 'configFields', pluginId: issue.pluginId, layer: issue.configLayer }
1865
+ : { name: 'configLayer', pluginId: issue.pluginId });
1866
+ }
1867
+ else if (entry.id === 'viewDependencies' && issue.pluginId) {
1868
+ go({ name: 'pluginDependencies', pluginId: issue.pluginId });
1869
+ }
1870
+ else if (entry.id === 'viewDependencyIssue' && entry.pluginId) {
1871
+ const dependencyIssue = props.controller.listUserIssues().find((candidate) => candidate.pluginId === entry.pluginId
1872
+ && candidate.issueId !== issue.issueId);
1873
+ if (dependencyIssue)
1874
+ go({ name: 'issueDetail', issueId: dependencyIssue.issueId });
1875
+ else
1876
+ go({ name: 'pluginDependencies', pluginId: entry.pluginId });
1877
+ }
1878
+ else
1879
+ setShowTechnical((value) => !value);
1880
+ };
1881
+ actions = issue.actions.map((entry) => ({
1882
+ label: entry.label,
1883
+ description: entry.description,
1884
+ status: entry.id === 'remove'
1885
+ || entry.id === 'keepUserScope' || entry.id === 'keepProjectScope' ? 'error' : undefined,
1886
+ run: () => runIssueAction(entry),
1887
+ }));
1888
+ const issuePlugin = issue.pluginId ? props.controller.getPluginOverview(issue.pluginId) : undefined;
1889
+ const issueOverview = issue.pluginId ? props.controller.getPluginDependencyOverview(issue.pluginId) : undefined;
1890
+ const manualMigration = issue.category === 'legacyState' && showTechnical;
1891
+ if (issue.category === 'dependencyStillRequired' && issuePlugin) {
1892
+ const blockingDependents = issue.platform && issue.scope
1893
+ ? props.controller.getBlockingDependents(issuePlugin.pluginId, issue.platform, issue.scope)
1894
+ : issueOverview?.dependents ?? [];
1895
+ title = `无法移除 ${issuePlugin.displayName}`;
1896
+ description = '仍有插件依赖它。没有提供危险绕过操作。';
1897
+ actions = [
1898
+ { label: '查看完整依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: issuePlugin.pluginId }) },
1899
+ { label: '返回插件详情', run: () => go({ name: 'pluginDetail', pluginId: issuePlugin.pluginId }) },
1900
+ ];
1901
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsx(Bordered, { children: _jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", blockingDependents.length, " \u4E2A\u5DF2\u5B89\u88C5\u63D2\u4EF6\u4ECD\u5728\u4F7F\u7528"] }) }), _jsx(Bordered, { title: "\u4F7F\u7528\u8FD9\u4E2A\u4F9D\u8D56\u7684\u63D2\u4EF6", children: blockingDependents.map((dependent) => _jsxs(Text, { children: [dependent.displayName, " ", _jsx(Text, { color: tuiTheme.colors.muted, children: dependentTargetLabel(dependent) })] }, dependent.pluginId)) }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1902
+ }
1903
+ else if (issue.category === 'dependencyTargetUnsupported' && issuePlugin) {
1904
+ const compatible = selectedPlatforms(issuePlugin, issue.scope ?? scope).filter((entry) => !entry.disabled);
1905
+ title = `无法添加到 ${issue.platform ? platformLabel(issue.platform) : '所选工具'}`;
1906
+ description = issue.dependencyPluginId
1907
+ ? `${props.controller.getPluginOverview(issue.dependencyPluginId)?.displayName ?? issue.dependencyPluginId} 不支持所选工具,因此完整插件计划不能运行。`
1908
+ : '完整插件计划不支持所选工具。';
1909
+ actions = [
1910
+ ...compatible.map((entry) => ({ label: `只添加到 ${platformLabel(entry.platform)}`, description: '返回位置页并使用完整依赖检查', run: () => {
1911
+ setScope(issue.scope ?? scope);
1912
+ setTools([entry.platform]);
1913
+ go({ name: 'installPlacement', pluginId: issuePlugin.pluginId });
1914
+ } })),
1915
+ { label: '返回调整工具', run: () => go({ name: 'installTools', pluginId: issuePlugin.pluginId }) },
1916
+ { label: '查看依赖关系', run: () => go({ name: 'pluginDependencies', pluginId: issuePlugin.pluginId }) },
1917
+ ];
1918
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", issue.dependencyPluginId ? `${props.controller.getPluginOverview(issue.dependencyPluginId)?.displayName ?? issue.dependencyPluginId} 不支持所选工具` : '工具不兼容'] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u53EF\u7528\u5DE5\u5177\u4ECD\u4FDD\u6301\u53EF\u9009\uFF0C\u4E0D\u517C\u5BB9\u5DE5\u5177\u4E0D\u4F1A\u8FDB\u5165\u786E\u8BA4\u9875\u3002" })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
1919
+ }
1920
+ else if (issue.category === 'dependencyGraphInvalid') {
1921
+ title = `暂时无法添加 ${issuePlugin?.displayName ?? issue.pluginId ?? '插件'}`;
1922
+ description = '来源中的依赖关系无效或安装记录不能安全验证。没有修改任何安装。';
1923
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.statusColors.error, children: ["\u00D7 ", issue.rootCode ?? issue.category] }), _jsx(Text, { color: tuiTheme.colors.muted, children: issue.explanation })] }), _jsx(ActionRows, { actions: actions, focus: focus }), showTechnical ? _jsx(Bordered, { title: "\u6280\u672F\u8BE6\u60C5", children: issue.technicalDetails.map((detail, index) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [detail.code, " \u00B7 ", detail.message] }, `${detail.code}:${index}`)) }) : _jsx(Text, { color: tuiTheme.colors.muted, children: "d \u67E5\u770B\u5B89\u5168\u9519\u8BEF\u4EE3\u7801" })] });
1924
+ }
1925
+ else {
1926
+ const sourceMissingCleanup = issue.category === 'sourceMissing' && issue.pluginId && issue.platform && issue.scope
1927
+ ? `aw cleanup ${issue.pluginId} --platform ${issue.platform} --scope ${issue.scope}`
1928
+ : undefined;
1929
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { title: "\u5F71\u54CD\u8303\u56F4", children: [issue.impact.map((line) => _jsx(Text, { children: line }, line)), sourceMissingCleanup ? _jsxs(_Fragment, { children: [_jsx(Text, { color: tuiTheme.statusColors.warning, children: "\u53EF\u6E05\u7406\u53EF\u4FE1\u7684 AW \u73B0\u573A\uFF1A" }), _jsx(Text, { children: sourceMissingCleanup }), _jsx(Text, { color: tuiTheme.statusColors.warning, children: "\u5982\u9700\u8DF3\u8FC7 dependent \u4FDD\u62A4\uFF0C\u5728\u547D\u4EE4\u672B\u5C3E\u6DFB\u52A0 --force\u3002" })] }) : null, issue.category === 'sourceIdentityChanged' ? _jsx(Text, { color: tuiTheme.statusColors.warning, children: "\u8BF7\u786E\u8BA4\u6765\u6E90\u53D8\u66F4\uFF0C\u6216\u6062\u590D\u539F identity\uFF1B\u5F53\u524D\u4E0D\u81EA\u52A8\u8FC1\u79FB\u6216\u6E05\u7406\u3002" }) : null, issue.placements ? _jsxs(Text, { children: ["\u4F4D\u7F6E\uFF1A", issue.placements.map(scopeLabel).join('、')] }) : null, issue.requiredFields ? _jsxs(Text, { children: ["\u5B57\u6BB5\uFF1A", issue.requiredFields.map((field) => field.title).join('、')] }) : null] }), _jsx(ActionRows, { actions: actions, focus: focus, offset: policy.mode === 'compact' ? scrollOffset : undefined, limit: policy.mode === 'compact' ? 4 : 7 }), manualMigration ? _jsxs(Bordered, { title: "\u624B\u52A8\u5904\u7406\u8BF4\u660E", children: [_jsx(Text, { children: "1. \u4E0D\u81EA\u52A8\u8FC1\u79FB\u65E7\u7248\u72B6\u6001\u3002" }), _jsx(Text, { children: "2. \u8FD0\u884C aw status --json\uFF0C\u8BB0\u5F55\u516C\u5F00\u9519\u8BEF code\u3002" }), _jsx(Text, { children: "3. \u5907\u4EFD\u76F8\u5173 AW \u914D\u7F6E\u3002" }), _jsx(Text, { children: "4. \u6309\u6280\u672F\u8BE6\u60C5\u4F7F\u7528\u53D7\u652F\u6301\u547D\u4EE4\u6216\u4EBA\u5DE5\u5904\u7406\u3002" }), _jsx(Text, { children: "5. \u518D\u6B21\u8FD0\u884C\u72B6\u6001\u68C0\u67E5\u3002" })] }) : showTechnical ? _jsx(Bordered, { title: "\u6280\u672F\u8BE6\u60C5", children: issue.technicalDetails.map((detail, index) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [detail.code, " \u00B7 ", detail.message] }, `${detail.code}:${index}`)) }) : _jsx(Text, { color: tuiTheme.colors.muted, children: "d \u6216\u201C\u6280\u672F\u8BE6\u60C5\u201D\u67E5\u770B\u5B89\u5168\u9519\u8BEF\u4EE3\u7801" })] });
1930
+ }
1931
+ }
1932
+ else if (route.name === 'review' && snapshot.activeReview) {
1933
+ const review = snapshot.activeReview;
1934
+ breadcrumb = `${review.subject.name} / ${review.title}`;
1935
+ title = `${snapshot.result?.state === 'stale' ? '状态已变化 · ' : ''}确认${review.title} ${review.subject.name}`;
1936
+ description = snapshot.result?.state === 'stale' ? '当前状态已变化。请检查新的变化后再次确认。' : review.description;
1937
+ actions = [
1938
+ ...review.changes.map((change) => ({
1939
+ label: `${reviewSymbol(change)} ${change.title}`,
1940
+ description: change.description,
1941
+ status: change.kind === 'remove' ? 'error'
1942
+ : change.kind === 'warning' ? 'warning'
1943
+ : change.kind === 'keep' || change.kind === 'add' ? 'success' : undefined,
1944
+ })),
1945
+ { label: review.primaryLabel, description: '执行前将再次检查当前状态', status: review.destructive ? 'error' : undefined, run: () => {
1946
+ if (operationBusy.current)
1947
+ return;
1948
+ operationBusy.current = true;
1949
+ return props.controller.executeOperation(review.reviewToken).then(() => {
1950
+ setFocus(0);
1951
+ setScrollOffset(0);
1952
+ }).finally(() => {
1953
+ operationBusy.current = false;
1954
+ });
1955
+ } },
1956
+ { label: '返回修改', run: review.operationKind === 'portableInstall' && snapshot.portableDraft
1957
+ ? async () => {
1958
+ if (await refreshPortableDraft(snapshot.portableDraft.pluginId))
1959
+ back();
1960
+ }
1961
+ : back },
1962
+ ];
1963
+ const reviewViewport = lineBudgetViewport(actions, focus, scrollOffset, policy.mode === 'compact' ? 8 : 15, [...review.changes.map((change) => change.group), undefined, undefined]);
1964
+ const reviewRows = (indexes) => indexes
1965
+ .filter((index) => index >= reviewViewport.offset
1966
+ && (reviewViewport.limit === undefined || index < reviewViewport.offset + reviewViewport.limit))
1967
+ .map((index) => _jsx(ActionRows, { actions: actions, focus: focus, offset: index, limit: 1 }, index));
1968
+ const groupedChanges = review.changes.some((change) => change.group);
1969
+ const groupTitle = {
1970
+ requested: review.operationKind === 'portableInstall' ? '已选择 Skills · 当前插件' : '你选择的插件',
1971
+ dependencies: review.operationKind === 'portableInstall' ? '已选择 Skills · 来自依赖插件' : '必需依赖',
1972
+ portableTarget: '安装目标',
1973
+ portableInputs: '准备内容',
1974
+ portableWarnings: '覆盖冲突',
1975
+ preserved: '保持不变',
1976
+ };
1977
+ content = _jsx(Box, { flexDirection: "column", children: _jsxs(Bordered, { children: [_jsxs(Text, { bold: true, children: [review.subject.name, " ", review.subject.sourceLabel, " ", review.subject.version] }), review.equivalentCommand ? _jsxs(_Fragment, { children: [_jsx(Text, { children: review.description }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u7B49\u4EF7\u547D\u4EE4\uFF1A", review.equivalentCommand] })] }) : null, groupedChanges ? Object.keys(groupTitle).map((group) => {
1978
+ const indexes = review.changes.map((change, index) => (change.group ?? 'requested') === group ? index : -1)
1979
+ .filter((index) => index >= 0);
1980
+ const rows = reviewRows(indexes);
1981
+ return rows.length > 0 ? _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: tuiTheme.colors.muted, children: groupTitle[group] }), rows] }, group) : null;
1982
+ }) : _jsx(_Fragment, { children: reviewRows(review.changes.map((_, index) => index)) }), reviewRows([review.changes.length, review.changes.length + 1]), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u25B8 \u6280\u672F\u8BE6\u60C5\uFF08d\uFF09" }), showTechnical ? review.technicalDetails?.map((detail) => _jsx(Text, { color: tuiTheme.colors.muted, children: detail }, detail)) : null] }) });
1983
+ }
1984
+ else if (route.name === 'progress') {
1985
+ const portableOperation = snapshot.activeReview?.operationKind === 'portableInstall';
1986
+ const configOperation = snapshot.activeReview?.operationKind === 'configSave'
1987
+ || snapshot.activeReview?.operationKind === 'configReset';
1988
+ const sourceOperation = snapshot.activeReview?.operationKind === 'sourceMutation';
1989
+ const issueOperation = snapshot.activeReview?.originIssue;
1990
+ breadcrumb = portableOperation ? '通用 Skills / 进度' : issueOperation ? '需要处理 / 进度' : sourceOperation ? '插件来源 / 保存' : configOperation ? '设置 / 进度' : '插件操作 / 进度';
1991
+ title = portableOperation ? '正在安装通用 Skills' : issueOperation ? '正在处理问题' : sourceOperation ? '正在保存并刷新来源' : configOperation ? '正在保存并应用设置' : '正在执行插件操作';
1992
+ description = issueOperation ? '完成后会使用新的状态 generation 重新检查问题。' : sourceOperation ? '先原子保存来源配置,再尽力刷新。刷新失败时配置仍保留并显示警告。' : configOperation ? '先保存所选层,再重新应用当前可见安装位置。' : '请勿关闭当前终端。';
1993
+ help = 'd 技术进度 · Ctrl-C 完成后退出';
1994
+ const progress = snapshot.progress;
1995
+ const elapsedSeconds = Math.max(0, Math.floor(((progress.elapsedMs
1996
+ ?? (progress.startedAtMs === undefined ? 0 : Date.now() - progress.startedAtMs))) / 1_000));
1997
+ const latestMessage = progress.messages.at(-1);
1998
+ const technicalMessages = progress.messages.slice(policy.mode === 'compact' ? -4 : -8);
1999
+ const dependencyProgress = snapshot.progressTargets.length > 0;
2000
+ const progressLabel = (entry) => {
2001
+ if (entry.state === 'kept')
2002
+ return { label: '已保留', status: 'success' };
2003
+ if (entry.state === 'completed')
2004
+ return { label: '已完成', status: 'success' };
2005
+ if (entry.state === 'running')
2006
+ return { label: '正在添加', status: 'info' };
2007
+ if (entry.state === 'failed')
2008
+ return { label: '失败', status: 'error' };
2009
+ if (entry.state === 'skipped')
2010
+ return { label: '未执行 · 依赖未完成', status: 'warning' };
2011
+ return { label: entry.relation === 'requested' ? '等待依赖' : '未开始', status: 'muted' };
2012
+ };
2013
+ content = portableOperation ? _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: progress.current?.phase.includes('检查') ? tuiTheme.colors.accent : tuiTheme.statusColors.success, children: [progress.current?.phase.includes('检查') ? '●' : '✓', " \u68C0\u67E5\u63D2\u4EF6\u4F9D\u8D56"] }), _jsxs(Text, { color: progress.current?.phase.includes('检查') ? tuiTheme.colors.accent : tuiTheme.statusColors.success, children: [progress.current?.phase.includes('检查') ? '●' : '✓', " \u751F\u6210\u5019\u9009 Skills"] }), _jsxs(Text, { color: progress.current?.phase.includes('安装') ? tuiTheme.colors.accent : progress.current?.phase.includes('验证') ? tuiTheme.statusColors.success : tuiTheme.colors.muted, children: [progress.current?.phase.includes('安装') ? '●' : progress.current?.phase.includes('验证') ? '✓' : '○', " \u5B89\u88C5\u9009\u62E9"] }), _jsxs(Text, { color: progress.current?.phase.includes('验证') ? tuiTheme.colors.accent : tuiTheme.colors.muted, children: [progress.current?.phase.includes('验证') ? '●' : '○', " \u9A8C\u8BC1"] }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u5F53\u524D\uFF1ASkills CLI" }), showTechnical ? technicalMessages.map((message, index) => _jsx(Text, { color: tuiTheme.colors.muted, children: message }, `${index}:${message}`)) : null] }) : _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: tuiTheme.statusColors.success, children: ["\u2713 ", sourceOperation ? '检查来源操作输入' : configOperation ? '验证设置值' : dependencyProgress ? '检查依赖关系' : '检查兼容性'] }), _jsxs(Text, { color: tuiTheme.colors.accent, children: ["\u25CF ", progress.current?.phase ?? (sourceOperation ? '保存来源配置并刷新' : configOperation ? '保存设置层并重新应用' : dependencyProgress ? '添加必需依赖' : '连接到所选工具')] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u25CB ", dependencyProgress ? '验证完整结果' : '验证结果'] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u5DF2\u7528\u65F6\uFF1A", elapsedSeconds, "s"] }), progress.current ? _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u5F53\u524D\uFF1A", progress.current.target] }) : null, dependencyProgress ? _jsx(Bordered, { children: snapshot.progressTargets.map((entry) => {
2014
+ const state = progressLabel(entry);
2015
+ const relation = entry.relation === 'requested' ? '你选择的插件' : entry.relation === 'direct' ? '直接依赖' : '间接依赖';
2016
+ const title = props.controller.getPluginOverview(entry.pluginId)?.displayName
2017
+ ?? entry.pluginId.split('/').at(-1);
2018
+ return _jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { children: [title, " ", _jsx(Text, { color: tuiTheme.colors.muted, children: relation })] }), _jsx(Text, { color: statusColor(state.status), children: state.label })] }, `${entry.pluginId}:${entry.platform}:${entry.scope}`);
2019
+ }) }) : null, !showTechnical && latestMessage ? _jsx(Text, { color: tuiTheme.colors.muted, children: latestMessage }) : null, showTechnical ? technicalMessages.map((message, index) => _jsx(Text, { color: tuiTheme.colors.muted, children: message }, `${index}:${message}`)) : null, exitAfterBusy ? _jsx(Text, { color: tuiTheme.statusColors.warning, children: "\u64CD\u4F5C\u5B8C\u6210\u540E\u9000\u51FA\u3002" }) : null] });
2020
+ }
2021
+ else if (route.name === 'result' && snapshot.result) {
2022
+ const result = snapshot.result;
2023
+ const partial = result.state === 'partial';
2024
+ const blocked = result.state === 'blocked';
2025
+ breadcrumb = '插件操作 / 结果';
2026
+ const action = result.state === 'succeeded' || result.state === 'partial'
2027
+ ? result.summary.action
2028
+ : undefined;
2029
+ const operationKind = 'operationKind' in result ? result.operationKind : undefined;
2030
+ const portableOperation = operationKind === 'portableInstall';
2031
+ const portableReceipt = result.state === 'succeeded' || result.state === 'partial'
2032
+ ? result.summary.portable : undefined;
2033
+ const portableInstallFailed = portableReceipt?.failureCode === 'portableSkillsCliInstallFailed';
2034
+ const portableOriginIsDetail = 'returnRoute' in result
2035
+ && result.returnRoute?.name === 'pluginDetail';
2036
+ const configOperation = operationKind === 'configSave' || operationKind === 'configReset'
2037
+ ? operationKind : undefined;
2038
+ const sourceOperation = operationKind === 'sourceMutation';
2039
+ const sourceRefreshWarning = sourceOperation && 'summary' in result
2040
+ && result.summary.messages?.some((message) => message.startsWith('警告:'));
2041
+ const issueOperation = result.originIssue;
2042
+ const completedTitle = {
2043
+ install: '✓ 插件添加完成',
2044
+ upgrade: '✓ 插件升级完成',
2045
+ reinstall: '✓ 插件重新安装完成',
2046
+ uninstall: '✓ 插件移除完成',
2047
+ cleanup: '✓ 插件清理完成',
2048
+ }[action ?? 'install'];
2049
+ title = portableOperation
2050
+ ? blocked ? '× 通用 Skills 安装被阻止'
2051
+ : partial ? '! 通用 Skills 结果未完全完成' : '✓ 通用 Skills 已安装'
2052
+ : blocked && result.writeMayHaveCompleted
2053
+ ? '× 写入结果未确认'
2054
+ : issueOperation
2055
+ ? blocked || partial ? '× 问题仍未解决' : '✓ 问题已处理'
2056
+ : sourceOperation
2057
+ ? blocked ? '× 来源操作未完成' : sourceRefreshWarning ? '! 来源配置已保存,但刷新失败' : '✓ 来源操作完成'
2058
+ : configOperation
2059
+ ? blocked
2060
+ ? '× 设置未确认保存'
2061
+ : partial ? '! 设置已保存,但部分应用失败' : '✓ 设置已保存并应用'
2062
+ : partial ? '! 插件操作只完成了一部分' : blocked ? '× 插件操作未完成' : completedTitle;
2063
+ description = portableOperation
2064
+ ? blocked
2065
+ ? result.code === 'operationReviewExpired' || result.code === 'reviewTicketExpired'
2066
+ ? 'Review 已过期,写入未开始。请返回修改并重新确认。'
2067
+ : result.code === 'operationReviewStale'
2068
+ ? '安装证据已变化,写入被阻止。请返回修改并重新确认。'
2069
+ : result.writeMayHaveCompleted
2070
+ ? '写入结果无法确认。请检查外部 Skills 状态后重试。'
2071
+ : '安装在写入前被阻止。请返回修改后重试。'
2072
+ : portableInstallFailed ? 'Skills CLI 安装失败,可能已有部分目标写入。可以修改 Agent 或使用原选择重试。' : partial ? '无法确认全部已选择 Skills。' : '所选 Skills 已通过安装验证。'
2073
+ : blocked && result.writeMayHaveCompleted
2074
+ ? result.message
2075
+ : issueOperation
2076
+ ? blocked
2077
+ ? result.code === 'issueVerificationFailed'
2078
+ ? '写入可能已完成,但当前无法确认。请重新检查状态。'
2079
+ : result.code === 'issueStillPresent'
2080
+ ? '写入已完成,但重新检查后原问题仍然存在。'
2081
+ : '操作被安全阻止。可返回问题详情后重试。'
2082
+ : '状态已重新加载;原问题已确认消失。'
2083
+ : sourceOperation
2084
+ ? blocked
2085
+ ? result.writeMayHaveCompleted
2086
+ ? '来源配置可能已保存。请重新检查来源列表;刷新失败不会撤销配置。'
2087
+ : '来源操作未执行。草稿已保留,可返回修改。'
2088
+ : sourceRefreshWarning
2089
+ ? '来源配置已保留。请根据警告修复来源后重试刷新。'
2090
+ : '来源配置已保存,刷新已完成。'
2091
+ : blocked ? result.message
2092
+ : configOperation ? '设置层已经更新。' : '状态已刷新。';
2093
+ const completedResult = result.state === 'succeeded' || result.state === 'partial'
2094
+ ? result
2095
+ : undefined;
2096
+ const pluginId = completedResult?.summary.pluginId
2097
+ ?? ('pluginId' in result ? result.pluginId : undefined);
2098
+ const failedTarget = completedResult?.summary.targets
2099
+ ?.filter((target) => target.state === 'failed')
2100
+ .sort((left, right) => [left.pluginId ?? '', left.platform, left.scope].join('\0')
2101
+ .localeCompare([right.pluginId ?? '', right.platform, right.scope].join('\0'), 'en'))[0];
2102
+ const failedPluginId = failedTarget?.pluginId ?? pluginId;
2103
+ const failedIssue = failedPluginId && failedTarget
2104
+ ? props.controller.listUserIssues().find((candidate) => (candidate.pluginId === failedPluginId || candidate.dependencyPluginId === failedPluginId)
2105
+ && candidate.platform === failedTarget.platform
2106
+ && (candidate.pluginId === failedPluginId
2107
+ ? candidate.scope === failedTarget.scope
2108
+ : candidate.dependencyPlatform === failedTarget.platform
2109
+ && candidate.dependencyScope === failedTarget.scope)
2110
+ && (candidate.category === 'incompleteInstallation' || candidate.category === 'diagnostic'
2111
+ || candidate.category === 'dependencyNotReady' || candidate.category === 'dependencyMissing'))
2112
+ : undefined;
2113
+ const failedTargetState = failedPluginId && failedTarget
2114
+ ? props.controller.getTargetState(failedPluginId, failedTarget.platform, failedTarget.scope)
2115
+ : undefined;
2116
+ const retryAction = partialRetryAction(operationKind, action, failedTargetState);
2117
+ const retryIssueAction = retryAction
2118
+ ? failedIssue?.actions.find((candidate) => (candidate.writeAction === retryAction || candidate.id === retryAction)
2119
+ && (candidate.pluginId ?? failedIssue.pluginId) === failedPluginId
2120
+ && (candidate.platform ?? failedIssue.platform) === failedTarget?.platform
2121
+ && (candidate.scope ?? failedIssue.scope) === failedTarget?.scope)
2122
+ : undefined;
2123
+ actions = portableOperation
2124
+ ? [
2125
+ { label: blocked || partial ? '返回修改' : portableOriginIsDetail ? '返回插件详情' : '返回插件预览', run: async () => {
2126
+ if (!pluginId) {
2127
+ leaveResult();
2128
+ return;
2129
+ }
2130
+ if (!blocked && !partial) {
2131
+ props.controller.discardPortableDraft();
2132
+ leaveResult();
2133
+ return;
2134
+ }
2135
+ if (!await refreshPortableDraft(pluginId))
2136
+ return;
2137
+ if (portableInstallFailed && portableReceipt?.agentMode === 'explicit') {
2138
+ props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableAgentInput', pluginId }, true);
2139
+ return;
2140
+ }
2141
+ props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableSkillSelect', pluginId }, true);
2142
+ } },
2143
+ { label: blocked || partial ? '重试' : '再安装其他 Skills…', run: async () => {
2144
+ if (!pluginId)
2145
+ return;
2146
+ if (blocked || partial) {
2147
+ props.controller.resetPortableFlowToPreview(pluginId, { name: 'portableSkillSelect', pluginId }, true);
2148
+ await beginPortableReview(pluginId, snapshot.portableDraft?.scope ?? 'project');
2149
+ return;
2150
+ }
2151
+ props.controller.resetPortableFlowToPreview(pluginId);
2152
+ await beginPortable(pluginId);
2153
+ } },
2154
+ { label: portableOriginIsDetail ? '返回插件详情' : '返回插件预览', run: () => { if (pluginId)
2155
+ props.controller.resetPortableFlowToPreview(pluginId); } },
2156
+ ]
2157
+ : issueOperation
2158
+ ? blocked || partial
2159
+ ? [
2160
+ { label: '返回问题详情', run: () => blocked
2161
+ ? leaveResult()
2162
+ : failedIssue
2163
+ ? leaveResult({ name: 'issueDetail', issueId: failedIssue.issueId })
2164
+ : pluginId && failedTarget
2165
+ ? leaveResult({ name: 'platform', pluginId, platform: failedTarget.platform })
2166
+ : leaveResult() },
2167
+ { label: '重新检查问题', run: () => leaveResult({ name: 'issueList' }) },
2168
+ ]
2169
+ : [
2170
+ { label: '查看问题列表', run: () => leaveResult({ name: 'issueList' }) },
2171
+ { label: '返回首页', run: () => leaveResult({ name: 'home' }) },
2172
+ ]
2173
+ : sourceOperation
2174
+ ? [{ label: blocked ? '返回修改' : '查看来源', run: () => blocked ? leaveResult() : leaveResult({ name: 'sourceList' }) }, { label: '返回首页', run: () => leaveResult({ name: 'home' }) }]
2175
+ : configOperation && blocked
2176
+ ? [
2177
+ { label: '返回设置', run: () => leaveResult() },
2178
+ { label: '返回首页', run: () => leaveResult({ name: 'home' }) },
2179
+ ]
2180
+ : [{ label: partial ? '查看失败项' : configOperation ? '查看插件' : action === 'uninstall' || action === 'cleanup' ? '返回插件详情' : '查看插件', run: () => {
2181
+ if (partial && pluginId && failedTarget) {
2182
+ if (failedIssue)
2183
+ leaveResult({ name: 'issueDetail', issueId: failedIssue.issueId });
2184
+ else
2185
+ leaveResult({ name: 'platform', pluginId, platform: failedTarget.platform });
2186
+ }
2187
+ else if (pluginId && snapshot.viewModel?.plugins.some((entry) => entry.pluginId === pluginId)) {
2188
+ leaveResult({ name: 'pluginDetail', pluginId });
2189
+ }
2190
+ else
2191
+ leaveResult({ name: 'pluginList', mode: 'installed' });
2192
+ } }, ...(partial && pluginId && failedTarget && retryAction ? [{
2193
+ label: '重试失败部分…',
2194
+ run: () => {
2195
+ const issueIntent = failedIssue && retryIssueAction
2196
+ ? issueWriteIntent(failedIssue, retryIssueAction)
2197
+ : undefined;
2198
+ return beginReview(issueIntent ?? {
2199
+ kind: 'pluginWrite',
2200
+ action: retryAction,
2201
+ pluginId: failedPluginId,
2202
+ platform: failedTarget.platform,
2203
+ scope: failedTarget.scope,
2204
+ });
2205
+ },
2206
+ }] : []), { label: '返回首页', run: () => leaveResult({ name: 'home' }) }];
2207
+ const visibleMessages = completedResult?.summary.messages?.slice(policy.mode === 'compact' ? -2 : -4) ?? [];
2208
+ const dependencyTargets = completedResult?.summary.targets?.some((target) => target.relation) ?? false;
2209
+ const resultTargetName = (target) => target.pluginId
2210
+ ? props.controller.getPluginOverview(target.pluginId)?.displayName
2211
+ ?? target.pluginId.split('/').at(-1)
2212
+ : `${platformLabel(target.platform)} · ${scopeLabel(target.scope)}`;
2213
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [result.state === 'blocked' ? _jsxs(Text, { color: tuiTheme.statusColors.error, children: [result.writeMayHaveCompleted ? '结果未确认' : '操作未完成', " \u00B7 ", result.code] }) : sourceOperation ? _jsx(Text, { color: tuiTheme.statusColors.success, children: "\u6765\u6E90\u914D\u7F6E\u5199\u5165\u5DF2\u5B8C\u6210\u3002" }) : completedResult && (completedResult.summary.targets?.length ?? 0) > 0 ? _jsxs(_Fragment, { children: [dependencyTargets ? _jsx(Text, { color: partial ? tuiTheme.statusColors.warning : tuiTheme.statusColors.success, children: partial ? '依赖安装没有全部完成' : '操作已完成' }) : null, completedResult.summary.targets.map((target) => { const state = operationTargetStateLabel(target.state); const relation = target.relation === 'requested' ? '你选择的插件' : target.relation === 'direct' ? '直接依赖' : target.relation === 'transitive' ? '间接依赖' : undefined; return _jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { children: [resultTargetName(target), relation ? _jsxs(Text, { color: tuiTheme.colors.muted, children: [" \u00B7 ", relation] }) : null] }), _jsx(Text, { color: statusColor(state.status), children: state.label })] }, `${target.pluginId ?? ''}:${target.platform}:${target.scope}`); }), partial && completedResult.summary.targets.some((target) => target.state === 'skipped') ? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u672A\u6267\u884C\u7684\u76EE\u6807\u6CA1\u6709\u5199\u5165\uFF1B\u5DF2\u5B8C\u6210\u6216\u5DF2\u4FDD\u7559\u7684\u76EE\u6807\u4FDD\u6301\u53EF\u7528\u3002" }) : null] }) : completedResult ? _jsx(Text, { color: portableInstallFailed ? tuiTheme.statusColors.error : partial ? tuiTheme.statusColors.warning : tuiTheme.statusColors.success, children: completedResult.summary.message }) : _jsx(Text, { color: tuiTheme.statusColors.warning, children: "\u72B6\u6001\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u786E\u8BA4\u3002" }), portableReceipt ? _jsxs(_Fragment, { children: [_jsxs(Text, { children: ["Skills\uFF1A", portableReceipt.selectedSkills.join('、')] }), _jsxs(Text, { children: ["Agent\uFF1A", portableReceipt.agentMode === 'automatic' ? '自动选择检测到的 Agent;未检测到时可能选择全部 Agent' : portableReceipt.requestedAgents.join('、')] }), _jsxs(Text, { children: ["\u8303\u56F4\uFF1A", scopeLabel(portableReceipt.scope)] }), portableReceipt.verificationUnknown ? _jsx(Text, { color: tuiTheme.colors.muted, children: "\u9A8C\u8BC1\u7ED3\u679C\uFF1A\u672A\u77E5" }) : _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u9A8C\u8BC1\u7F3A\u9879\uFF1A", portableReceipt.missing.length] }), portableReceipt.recovery ? _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u6062\u590D\uFF1A", portableReceipt.recovery] }) : null, _jsxs(Text, { color: tuiTheme.colors.muted, children: ["Materialization\uFF1A", portableReceipt.materializationDigest] })] }) : null, completedResult?.summary.elapsedMs !== undefined ? _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u5DF2\u7528\u65F6\uFF1A", Math.floor(completedResult.summary.elapsedMs / 1_000), "s"] }) : null, visibleMessages.map((message, index) => _jsx(Text, { color: tuiTheme.colors.muted, children: message }, `${index}:${message}`))] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
2214
+ }
2215
+ else if (route.name === 'pluginAbout' && plugin) {
2216
+ breadcrumb = `${plugin.displayName} / 关于`;
2217
+ title = plugin.displayName;
2218
+ description = plugin.description;
2219
+ actions = [{ label: '查看技术详情', run: () => go({ name: 'pluginTechnical', pluginId: plugin.pluginId }) }, { label: '查看来源', run: () => go({ name: 'sourceDetail', sourceId: plugin.sourceId }) }];
2220
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u7248\u672C ", _jsx(Text, { color: tuiTheme.colors.text, children: plugin.version ?? '未知' })] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u6765\u6E90 ", _jsx(Text, { color: tuiTheme.colors.text, children: plugin.sourceLabel })] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u53EF\u7528\u5DE5\u5177 ", _jsx(Text, { color: tuiTheme.colors.text, children: plugin.supportedPlatforms.map(platformLabel).join('、') })] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u914D\u7F6E ", _jsxs(Text, { color: tuiTheme.colors.text, children: [plugin.configFieldCount, " \u4E2A\u5B57\u6BB5"] })] })] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
2221
+ }
2222
+ else if (route.name === 'pluginTechnical' && plugin) {
2223
+ breadcrumb = `${plugin.displayName} / 技术详情`;
2224
+ context = '只读';
2225
+ title = '安装与身份详情';
2226
+ description = '默认页面不会展示这些内部信息。';
2227
+ actions = [{ label: '返回插件', run: back }];
2228
+ content = _jsxs(Box, { flexDirection: "column", children: [_jsxs(Bordered, { children: [_jsxs(Text, { color: tuiTheme.colors.muted, children: ["canonical pluginId ", _jsx(Text, { color: tuiTheme.colors.text, children: plugin.pluginId })] }), _jsxs(Text, { color: tuiTheme.colors.muted, children: ["\u6765\u6E90 ", _jsx(Text, { color: tuiTheme.colors.text, children: plugin.sourceId })] }), plugin.installedPlacements.map((placement) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [platformLabel(placement.platform), " target ", _jsx(Text, { color: tuiTheme.colors.text, children: placementStatus(placement).value })] }, placement.platform)), plugin.technicalComponents.map((component) => _jsxs(Text, { color: tuiTheme.colors.muted, children: [platformLabel(component.platform), " \u00B7 ", scopeLabel(component.scope), " \u00B7 ", component.key, " \u00B7 ", component.state, component.errorCode ? ` · ${component.errorCode}` : ''] }, `${component.platform}:${component.scope}:${component.key}`))] }), _jsx(ActionRows, { actions: actions, focus: focus })] });
2229
+ }
2230
+ else {
2231
+ breadcrumb = '后续阶段';
2232
+ title = '此业务页将在后续阶段接入';
2233
+ description = '当前阶段只实现插件发现、安装和维护页面。';
2234
+ actions = [{ label: '返回上一页', run: back }];
2235
+ content = _jsx(ActionRows, { actions: actions, focus: focus });
2236
+ }
2237
+ const runAction = (action) => {
2238
+ try {
2239
+ const pending = action?.run?.();
2240
+ if (pending)
2241
+ void pending.catch(() => {
2242
+ setActionError('操作未完成。请重新检查后重试。');
2243
+ });
2244
+ }
2245
+ catch {
2246
+ setActionError('操作未完成。请重新检查后重试。');
2247
+ }
2248
+ };
2249
+ useInput((input, key) => {
2250
+ if (busy) {
2251
+ if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
2252
+ setExitAfterBusy(true);
2253
+ }
2254
+ else if (!tooSmall && input.toLowerCase() === 'd') {
2255
+ setShowTechnical((value) => !value);
2256
+ }
2257
+ return;
2258
+ }
2259
+ if (tooSmall) {
2260
+ if (input.toLowerCase() === 'q' || (key.ctrl && input.toLowerCase() === 'c')) {
2261
+ requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
2262
+ }
2263
+ return;
2264
+ }
2265
+ if (overlay) {
2266
+ if (key.ctrl && input.toLowerCase() === 'c') {
2267
+ requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
2268
+ return;
2269
+ }
2270
+ if (key.escape || input.toLowerCase() === 'b' || input === '?') {
2271
+ setOverlay(undefined);
2272
+ return;
2273
+ }
2274
+ if (key.leftArrow || key.rightArrow || key.upArrow || key.downArrow) {
2275
+ setOverlayFocus((value) => value === 0 ? 1 : 0);
2276
+ return;
2277
+ }
2278
+ if (key.return) {
2279
+ if (overlay === 'help' || overlayFocus === 0)
2280
+ setOverlay(undefined);
2281
+ else if (overlay === 'discard') {
2282
+ setOverlay(undefined);
2283
+ setDirty(false);
2284
+ setDraftUpdates({});
2285
+ setDraftRemoves([]);
2286
+ const discardedSourceDraft = sourceDraft;
2287
+ setSourceDraft(undefined);
2288
+ if (discardForExit) {
2289
+ requestExit({ code: 0, reason: 'user' });
2290
+ }
2291
+ else if (discardedSourceDraft) {
2292
+ props.controller.discardSourceDraft(discardedSourceDraft.owner, discardedSourceDraft.mode === 'update'
2293
+ ? { name: 'sourceDetail', sourceId: discardedSourceDraft.sourceId }
2294
+ : { name: 'sourceList' });
2295
+ }
2296
+ else {
2297
+ back();
2298
+ }
2299
+ }
2300
+ else
2301
+ requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
2302
+ }
2303
+ return;
2304
+ }
2305
+ if (key.ctrl && input.toLowerCase() === 'c') {
2306
+ requestExit({ code: snapshot.phase === 'error' ? 1 : 0, reason: snapshot.phase === 'error' ? 'initialError' : 'user' });
2307
+ return;
2308
+ }
2309
+ if (route.name === 'portableAgentInput') {
2310
+ if (key.escape) {
2311
+ back();
2312
+ return;
2313
+ }
2314
+ if (key.return) {
2315
+ try {
2316
+ const normalizedInput = portableAgentInput.trim();
2317
+ const agents = normalizePortableAgents(normalizedInput ? normalizedInput.split(/\s+/u) : []);
2318
+ props.controller.setPortableAgentMode('explicit', agents);
2319
+ setInputError(undefined);
2320
+ go({ name: 'portableScope', pluginId: route.pluginId });
2321
+ }
2322
+ catch (error) {
2323
+ setInputError(error instanceof Error ? error.message.replace(/^portableInvalidAgentSelection:\s*/u, '') : 'Agent ID 无效。');
2324
+ }
2325
+ return;
2326
+ }
2327
+ if (key.backspace || key.delete)
2328
+ setPortableAgentInput((value) => value.slice(0, -1));
2329
+ else if (input && !key.ctrl && !key.meta && input >= ' ')
2330
+ setPortableAgentInput((value) => value + input);
2331
+ setInputError(undefined);
2332
+ return;
2333
+ }
2334
+ if (route.name === 'sourceAdd'
2335
+ && route.step !== 'type'
2336
+ && route.step !== 'refPolicy'
2337
+ && sourceDraft?.owner === route.owner) {
2338
+ if (key.escape) {
2339
+ setOverlayFocus(0);
2340
+ setDiscardForExit(false);
2341
+ setOverlay('discard');
2342
+ return;
2343
+ }
2344
+ if (key.return) {
2345
+ let error;
2346
+ if (route.step === 'id') {
2347
+ if (!CATALOG_SOURCE_ID_PATTERN.test(sourceInput))
2348
+ error = '格式必须是小写字母、数字或连字符,最多 32 字符。';
2349
+ else if (props.controller.listSources().some((source) => source.id === sourceInput))
2350
+ error = '这个来源 ID 已存在。';
2351
+ }
2352
+ else if (route.step === 'location') {
2353
+ if (sourceDraft.type === 'path') {
2354
+ if (!path.isAbsolute(sourceInput))
2355
+ error = '必须输入绝对路径。';
2356
+ }
2357
+ else {
2358
+ try {
2359
+ canonicalizeCatalogGitUrl(sourceInput);
2360
+ }
2361
+ catch {
2362
+ error = 'Git URL 不符合来源规范,或包含不允许的认证信息。';
2363
+ }
2364
+ }
2365
+ }
2366
+ else if (route.step === 'ref') {
2367
+ if (sourceInput.trim() === '' || sourceInput !== sourceInput.trim() || /[\s\u0000-\u001f\u007f]/u.test(sourceInput))
2368
+ error = '必须输入不含空白或控制字符的 Git ref。';
2369
+ }
2370
+ else if (sourceInput !== '') {
2371
+ const normalized = path.posix.normalize(sourceInput);
2372
+ if (sourceInput.includes('\\') || path.posix.isAbsolute(sourceInput)
2373
+ || normalized === '.' || normalized === '..' || normalized.startsWith('../')) {
2374
+ error = '必须输入来源内部的相对 POSIX 路径。';
2375
+ }
2376
+ }
2377
+ if (error) {
2378
+ setSourceInputError(error);
2379
+ return;
2380
+ }
2381
+ const next = { ...sourceDraft };
2382
+ if (route.step === 'id')
2383
+ next.sourceId = sourceInput;
2384
+ else if (route.step === 'location')
2385
+ next.location = sourceInput;
2386
+ else if (route.step === 'ref')
2387
+ next.ref = sourceInput;
2388
+ else
2389
+ next.subdir = sourceInput === '' ? '' : path.posix.normalize(sourceInput).replace(/^\.\//u, '').replace(/\/$/u, '');
2390
+ setSourceDraft(next);
2391
+ setSourceInputError(undefined);
2392
+ if (route.step === 'id')
2393
+ go({ name: 'sourceAdd', step: 'location', owner: route.owner });
2394
+ else if (route.step === 'location') {
2395
+ if (next.mode === 'update')
2396
+ void beginSourceUpdateReview(next).catch(() => setActionError('来源修改未通过验证。'));
2397
+ else if (next.type === 'git')
2398
+ go({ name: 'sourceAdd', step: 'refPolicy', owner: route.owner });
2399
+ else
2400
+ void beginSourceAddReviewWith(next).catch(() => setActionError('来源未通过验证。请检查输入后重试。'));
2401
+ }
2402
+ else if (route.step === 'ref') {
2403
+ if (next.mode === 'update')
2404
+ void beginSourceUpdateReview(next).catch(() => setActionError('来源修改未通过验证。'));
2405
+ else
2406
+ go({ name: 'sourceAdd', step: 'subdir', owner: route.owner });
2407
+ }
2408
+ else if (next.mode === 'update')
2409
+ void beginSourceUpdateReview(next).catch(() => setActionError('来源修改未通过验证。'));
2410
+ else
2411
+ void beginSourceAddReviewWith(next).catch(() => setActionError('来源未通过验证。请检查输入后重试。'));
2412
+ return;
2413
+ }
2414
+ if (key.backspace || key.delete)
2415
+ setSourceInput((value) => value.slice(0, -1));
2416
+ else if (input && !key.ctrl && !key.meta && input >= ' ')
2417
+ setSourceInput((value) => value + input);
2418
+ setSourceInputError(undefined);
2419
+ return;
2420
+ }
2421
+ if (route.name === 'configFieldEditor'
2422
+ && route.editor !== 'enum'
2423
+ && route.editor !== 'boolean') {
2424
+ if (key.escape) {
2425
+ back();
2426
+ return;
2427
+ }
2428
+ if (input === '\u0015' || (input.toLowerCase() === 'u' && key.ctrl)) {
2429
+ removeDraft(route.fieldKey);
2430
+ back();
2431
+ return;
2432
+ }
2433
+ if (key.return && activeField) {
2434
+ if (route.editor === 'sensitive' && inputValue.length === 0) {
2435
+ back();
2436
+ return;
2437
+ }
2438
+ if (route.editor === 'number') {
2439
+ const number = Number(inputValue);
2440
+ if (!Number.isFinite(number)) {
2441
+ setInputError('必须输入有限数字。');
2442
+ return;
2443
+ }
2444
+ if (activeField.min !== undefined && number < activeField.min
2445
+ || activeField.max !== undefined && number > activeField.max) {
2446
+ setInputError(`必须在 ${activeField.min ?? '不限'}–${activeField.max ?? '不限'} 范围内。`);
2447
+ return;
2448
+ }
2449
+ updateDraft(activeField.key, number);
2450
+ }
2451
+ else
2452
+ updateDraft(activeField.key, inputValue);
2453
+ back();
2454
+ return;
2455
+ }
2456
+ if (key.backspace || key.delete)
2457
+ setInputValue((value) => value.slice(0, -1));
2458
+ else if (input && !key.ctrl && !key.meta && input >= ' ')
2459
+ setInputValue((value) => value + input);
2460
+ setInputError(undefined);
2461
+ return;
2462
+ }
2463
+ if (searchMode && route.name === 'pluginList') {
2464
+ if (key.escape) {
2465
+ setSearchMode(false);
2466
+ return;
2467
+ }
2468
+ if (key.backspace || key.delete)
2469
+ setQuery((value) => value.slice(0, -1));
2470
+ else if (input && !key.ctrl && !key.meta && input >= ' ')
2471
+ setQuery((value) => value + input);
2472
+ setFocus(0);
2473
+ setScrollOffset(0);
2474
+ return;
2475
+ }
2476
+ if (key.tab && (route.name === 'configLayer'
2477
+ || route.name === 'configFields'
2478
+ || route.name === 'configFieldEditor'
2479
+ || route.name === 'configPresets')) {
2480
+ return;
2481
+ }
2482
+ if (input === '?') {
2483
+ setOverlayFocus(0);
2484
+ setOverlay('help');
2485
+ }
2486
+ else if (input.toLowerCase() === 'q') {
2487
+ setOverlayFocus(0);
2488
+ setDiscardForExit(dirty);
2489
+ setOverlay(dirty ? 'discard' : 'exit');
2490
+ }
2491
+ else if (input.toLowerCase() === 'd' && (route.name === 'review'
2492
+ || route.name === 'issueDetail'
2493
+ || route.name === 'configUnavailable'
2494
+ || route.name === 'pluginDependencies'
2495
+ || route.name === 'portableDependencies'
2496
+ || route.name === 'portableBlocker')) {
2497
+ setShowTechnical((value) => !value);
2498
+ }
2499
+ else if ((key.escape || input.toLowerCase() === 'b')
2500
+ && (route.name === 'configFieldEditor'
2501
+ || route.name === 'configPresets'
2502
+ || (route.name === 'configFields' && Boolean(route.groupId)))) {
2503
+ back();
2504
+ }
2505
+ else if (key.escape || input.toLowerCase() === 'b') {
2506
+ if (dirty) {
2507
+ setOverlayFocus(0);
2508
+ setDiscardForExit(false);
2509
+ setOverlay('discard');
2510
+ }
2511
+ else
2512
+ back();
2513
+ }
2514
+ else if (route.name === 'pluginList' && input === '/') {
2515
+ setSearchMode(true);
2516
+ }
2517
+ else if (route.name === 'configFields' && route.layer.scope !== 'effective' && input.toLowerCase() === 's') {
2518
+ if (dirty)
2519
+ void beginConfigSave();
2520
+ }
2521
+ else if (route.name === 'configFields' && route.layer.scope !== 'effective' && input.toLowerCase() === 'u') {
2522
+ const key = actions[focus]?.configKey;
2523
+ if (key)
2524
+ removeDraft(key);
2525
+ }
2526
+ else if (input.toLowerCase() === 'r' && route.name === 'sourceDetail') {
2527
+ void props.controller.refreshSource(route.sourceId);
2528
+ }
2529
+ else if (input.toLowerCase() === 'r' && route.name === 'sourceList') {
2530
+ const source = props.controller.listSources()[focus];
2531
+ if (source)
2532
+ void props.controller.refreshSource(source.id);
2533
+ }
2534
+ else if (route.name === 'pluginList' && (key.leftArrow || key.rightArrow || input === '[' || input === ']')) {
2535
+ const tabs = ['all', ...props.controller.listSources().map((source) => source.id)];
2536
+ const current = route.sourceId ?? 'all';
2537
+ const selectedPluginId = props.controller.listPlugins({
2538
+ mode: route.mode,
2539
+ ...(current !== 'all' ? { sourceId: current } : {}),
2540
+ ...(query ? { query } : {}),
2541
+ })[focus]?.pluginId;
2542
+ const direction = key.leftArrow || input === '[' ? -1 : 1;
2543
+ const index = tabs.indexOf(current);
2544
+ const next = tabs[(index + direction + tabs.length) % tabs.length];
2545
+ const nextItems = props.controller.listPlugins({ mode: route.mode, ...(next !== 'all' ? { sourceId: next } : {}), ...(query ? { query } : {}) });
2546
+ const retained = selectedPluginId ? nextItems.findIndex((item) => item.pluginId === selectedPluginId) : -1;
2547
+ props.controller.replace({ name: 'pluginList', mode: route.mode, ...(next !== 'all' ? { sourceId: next } : {}) }, { ...snapshot.router.current.context, sourceTabId: next });
2548
+ setFocus(retained >= 0 ? retained : 0);
2549
+ setScrollOffset(retained >= 4 ? retained - 3 : 0);
2550
+ }
2551
+ else if (input.toLowerCase() === 'a' && route.name === 'portableSkillSelect') {
2552
+ props.controller.selectAllPortableSkills();
2553
+ }
2554
+ else if (input === ' ' && route.name === 'portableSkillSelect') {
2555
+ const candidate = snapshot.portableDraft?.candidates[focus];
2556
+ if (candidate)
2557
+ props.controller.togglePortableSkill(candidate.name);
2558
+ }
2559
+ else if (key.upArrow || input.toLowerCase() === 'k') {
2560
+ setFocus((value) => {
2561
+ const next = moveFocus(actions, value, -1);
2562
+ const viewport = route.name === 'portableSkillSelect' && snapshot.portableDraft
2563
+ ? portableSkillCandidateViewport(actions.slice(0, snapshot.portableDraft.candidates.length), snapshot.portableDraft.candidates, policy.mode, next, scrollOffset)
2564
+ : actionViewport(route.name, actions, policy.mode, next, scrollOffset);
2565
+ setScrollOffset(viewport.offset);
2566
+ return next;
2567
+ });
2568
+ }
2569
+ else if (key.downArrow || input.toLowerCase() === 'j') {
2570
+ setFocus((value) => {
2571
+ const next = moveFocus(actions, value, 1);
2572
+ const viewport = route.name === 'portableSkillSelect' && snapshot.portableDraft
2573
+ ? portableSkillCandidateViewport(actions.slice(0, snapshot.portableDraft.candidates.length), snapshot.portableDraft.candidates, policy.mode, next, scrollOffset)
2574
+ : actionViewport(route.name, actions, policy.mode, next, scrollOffset);
2575
+ setScrollOffset(viewport.offset);
2576
+ return next;
2577
+ });
2578
+ }
2579
+ else if (input === ' ' && route.name === 'installTools' && route.view !== 'blocked') {
2580
+ runAction(actions[focus]);
2581
+ }
2582
+ else if (key.return && route.name === 'installTools' && route.view !== 'blocked') {
2583
+ if (tools.length > 0) {
2584
+ setDirty(false);
2585
+ back();
2586
+ }
2587
+ }
2588
+ else if (key.return) {
2589
+ runAction(actions[focus]);
2590
+ }
2591
+ });
2592
+ if (tooSmall) {
2593
+ return (_jsx(ScreenFrame, { title: "\u7EC8\u7AEF\u7A7A\u95F4\u4E0D\u8DB3", breadcrumb: "\u7EC8\u7AEF\u7A7A\u95F4\u4E0D\u8DB3", context: `${size.columns} × ${size.rows}`, help: "q \u9000\u51FA", size: size, children: _jsxs(Bordered, { children: [_jsx(Text, { bold: true, children: "\u2194 \u8BF7\u5C06\u7EC8\u7AEF\u8C03\u6574\u5230\u81F3\u5C11 60 \u00D7 18" }), _jsx(Text, { color: tuiTheme.colors.muted, children: "\u8C03\u6574\u540E\u4F1A\u8FD4\u56DE\u539F\u9875\u9762\uFF0C\u4E0D\u4F1A\u4E22\u5931\u5F53\u524D\u9009\u62E9\u3002" })] }) }));
2594
+ }
2595
+ if (actionError)
2596
+ description = actionError;
2597
+ const base = (_jsx(ScreenFrame, { title: title, description: description, breadcrumb: breadcrumb, context: context, help: help, size: size, children: content }));
2598
+ if (!overlay)
2599
+ return base;
2600
+ const helpRows = [
2601
+ ['↑ / ↓', '移动当前唯一焦点'],
2602
+ ['Enter', '打开、选择或继续'],
2603
+ ['Esc / b', '返回上一页'],
2604
+ ...(route.name === 'pluginList' ? [['← / →', '切换来源'], ['/', '进入搜索']] : []),
2605
+ ...(route.name === 'installTools' ? [['Space', '切换工具选择']] : []),
2606
+ ...(route.name === 'review' || route.name === 'progress' ? [['d', '打开技术详情']] : []),
2607
+ ['?', '关闭帮助'],
2608
+ ];
2609
+ return (_jsxs(Box, { position: "relative", width: size.columns, height: size.rows, children: [_jsx(Box, { children: base }), _jsxs(Box, { position: "absolute", marginTop: 5, marginLeft: Math.max(2, Math.floor(size.columns / 5)), width: Math.max(40, Math.floor(size.columns * 0.6)), flexDirection: "column", borderStyle: "round", borderColor: tuiTheme.colors.accent, paddingX: 1, children: [_jsx(Text, { bold: true, children: overlay === 'help' ? '当前页快捷键' : overlay === 'discard' ? '放弃未保存修改?' : '退出控制台?' }), overlay === 'help'
2610
+ ? _jsx(_Fragment, { children: helpRows.map(([key, value]) => _jsxs(Text, { children: [key.padEnd(9), value] }, key)) })
2611
+ : _jsx(Text, { color: tuiTheme.colors.muted, children: overlay === 'discard' ? '放弃后将返回上一页。' : '进行中的写操作会先完成。' }), _jsxs(Text, { bold: true, color: overlayFocus === 0 ? tuiTheme.colors.accent : undefined, children: ["\u203A ", overlay === 'help' ? '关闭' : overlay === 'discard' ? '继续编辑' : '继续使用'] }), overlay === 'exit' || overlay === 'discard' ? _jsxs(Text, { bold: true, color: overlayFocus === 1 ? tuiTheme.statusColors.error : undefined, children: [overlayFocus === 1 ? '› ' : ' ', overlay === 'discard' ? '放弃修改' : '退出'] }) : null] })] }));
2612
+ }
2613
+ //# sourceMappingURL=console-app-v4.js.map