@lesliechan721/aw 0.1.0-beta.10

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 (508) hide show
  1. package/dist/authoring/assets/aw-config.d.mts +51 -0
  2. package/dist/authoring/assets/aw-config.mjs +1025 -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/init-plugin.d.mts +2 -0
  8. package/dist/authoring/scripts/init-plugin.mjs +487 -0
  9. package/dist/authoring/scripts/init-plugin.mjs.map +1 -0
  10. package/dist/authoring/scripts/lib/javascript-import-scanner.d.mts +4 -0
  11. package/dist/authoring/scripts/lib/javascript-import-scanner.mjs +261 -0
  12. package/dist/authoring/scripts/lib/javascript-import-scanner.mjs.map +1 -0
  13. package/dist/authoring/scripts/lib/plugin-root.d.mts +26 -0
  14. package/dist/authoring/scripts/lib/plugin-root.mjs +125 -0
  15. package/dist/authoring/scripts/lib/plugin-root.mjs.map +1 -0
  16. package/dist/authoring/scripts/lib/plugin-version-check.d.mts +69 -0
  17. package/dist/authoring/scripts/lib/plugin-version-check.mjs +563 -0
  18. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -0
  19. package/dist/authoring/scripts/lib/semver.d.mts +9 -0
  20. package/dist/authoring/scripts/lib/semver.mjs +63 -0
  21. package/dist/authoring/scripts/lib/semver.mjs.map +1 -0
  22. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +111 -0
  23. package/dist/authoring/scripts/lib/skill-scaffold.mjs +353 -0
  24. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -0
  25. package/dist/authoring/scripts/plugin-status.d.mts +2 -0
  26. package/dist/authoring/scripts/plugin-status.mjs +93 -0
  27. package/dist/authoring/scripts/plugin-status.mjs.map +1 -0
  28. package/dist/authoring/scripts/remove-plugin.d.mts +2 -0
  29. package/dist/authoring/scripts/remove-plugin.mjs +367 -0
  30. package/dist/authoring/scripts/remove-plugin.mjs.map +1 -0
  31. package/dist/authoring/scripts/validate-plugin.d.mts +2 -0
  32. package/dist/authoring/scripts/validate-plugin.mjs +3465 -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/authoring/templates/aw-config-runtime.d.mts +2 -0
  38. package/dist/authoring/templates/aw-config-runtime.mjs +407 -0
  39. package/dist/authoring/templates/aw-config-runtime.mjs.map +1 -0
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.js +8 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/scripts/aw-skill-eval-materializer.d.ts +24 -0
  44. package/dist/scripts/aw-skill-eval-materializer.js +309 -0
  45. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
  46. package/dist/scripts/bundle-plugins.d.ts +17 -0
  47. package/dist/scripts/bundle-plugins.js +126 -0
  48. package/dist/scripts/bundle-plugins.js.map +1 -0
  49. package/dist/scripts/plugin-staging.d.ts +26 -0
  50. package/dist/scripts/plugin-staging.js +76 -0
  51. package/dist/scripts/plugin-staging.js.map +1 -0
  52. package/dist/scripts/test-plugin-scripts.d.ts +7 -0
  53. package/dist/scripts/test-plugin-scripts.js +610 -0
  54. package/dist/scripts/test-plugin-scripts.js.map +1 -0
  55. package/dist/src/adapters/helpers.d.ts +44 -0
  56. package/dist/src/adapters/helpers.js +95 -0
  57. package/dist/src/adapters/helpers.js.map +1 -0
  58. package/dist/src/authoring/workspace-commands.d.ts +70 -0
  59. package/dist/src/authoring/workspace-commands.js +158 -0
  60. package/dist/src/authoring/workspace-commands.js.map +1 -0
  61. package/dist/src/commands/aw-capabilities.d.ts +78 -0
  62. package/dist/src/commands/aw-capabilities.js +254 -0
  63. package/dist/src/commands/aw-capabilities.js.map +1 -0
  64. package/dist/src/commands/catalog-dev-source.d.ts +120 -0
  65. package/dist/src/commands/catalog-dev-source.js +434 -0
  66. package/dist/src/commands/catalog-dev-source.js.map +1 -0
  67. package/dist/src/commands/catalog-generation.d.ts +16 -0
  68. package/dist/src/commands/catalog-generation.js +9 -0
  69. package/dist/src/commands/catalog-generation.js.map +1 -0
  70. package/dist/src/commands/catalog-publication-inspection.d.ts +138 -0
  71. package/dist/src/commands/catalog-publication-inspection.js +711 -0
  72. package/dist/src/commands/catalog-publication-inspection.js.map +1 -0
  73. package/dist/src/commands/catalog-publication-journal.d.ts +56 -0
  74. package/dist/src/commands/catalog-publication-journal.js +215 -0
  75. package/dist/src/commands/catalog-publication-journal.js.map +1 -0
  76. package/dist/src/commands/catalog-publication-target.d.ts +18 -0
  77. package/dist/src/commands/catalog-publication-target.js +95 -0
  78. package/dist/src/commands/catalog-publication-target.js.map +1 -0
  79. package/dist/src/commands/catalog-publish.d.ts +103 -0
  80. package/dist/src/commands/catalog-publish.js +804 -0
  81. package/dist/src/commands/catalog-publish.js.map +1 -0
  82. package/dist/src/commands/catalog-source.d.ts +134 -0
  83. package/dist/src/commands/catalog-source.js +377 -0
  84. package/dist/src/commands/catalog-source.js.map +1 -0
  85. package/dist/src/commands/catalog-status.d.ts +56 -0
  86. package/dist/src/commands/catalog-status.js +217 -0
  87. package/dist/src/commands/catalog-status.js.map +1 -0
  88. package/dist/src/commands/catalog-store.d.ts +80 -0
  89. package/dist/src/commands/catalog-store.js +348 -0
  90. package/dist/src/commands/catalog-store.js.map +1 -0
  91. package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
  92. package/dist/src/commands/catalog-workspace-resolution.js +42 -0
  93. package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
  94. package/dist/src/commands/console.d.ts +3 -0
  95. package/dist/src/commands/console.js +44 -0
  96. package/dist/src/commands/console.js.map +1 -0
  97. package/dist/src/commands/dependency-operation-plan.d.ts +58 -0
  98. package/dist/src/commands/dependency-operation-plan.js +261 -0
  99. package/dist/src/commands/dependency-operation-plan.js.map +1 -0
  100. package/dist/src/commands/init.d.ts +25 -0
  101. package/dist/src/commands/init.js +42 -0
  102. package/dist/src/commands/init.js.map +1 -0
  103. package/dist/src/commands/offline-cleanup-evidence.d.ts +59 -0
  104. package/dist/src/commands/offline-cleanup-evidence.js +163 -0
  105. package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
  106. package/dist/src/commands/operation-plan.d.ts +126 -0
  107. package/dist/src/commands/operation-plan.js +47 -0
  108. package/dist/src/commands/operation-plan.js.map +1 -0
  109. package/dist/src/commands/operation-planner.d.ts +46 -0
  110. package/dist/src/commands/operation-planner.js +139 -0
  111. package/dist/src/commands/operation-planner.js.map +1 -0
  112. package/dist/src/commands/options.d.ts +70 -0
  113. package/dist/src/commands/options.js +194 -0
  114. package/dist/src/commands/options.js.map +1 -0
  115. package/dist/src/commands/plugin-capabilities.d.ts +22 -0
  116. package/dist/src/commands/plugin-capabilities.js +159 -0
  117. package/dist/src/commands/plugin-capabilities.js.map +1 -0
  118. package/dist/src/commands/rebuild-command.d.ts +88 -0
  119. package/dist/src/commands/rebuild-command.js +3626 -0
  120. package/dist/src/commands/rebuild-command.js.map +1 -0
  121. package/dist/src/commands/rebuild-integration.d.ts +190 -0
  122. package/dist/src/commands/rebuild-integration.js +4439 -0
  123. package/dist/src/commands/rebuild-integration.js.map +1 -0
  124. package/dist/src/commands/rebuild-operations.d.ts +100 -0
  125. package/dist/src/commands/rebuild-operations.js +625 -0
  126. package/dist/src/commands/rebuild-operations.js.map +1 -0
  127. package/dist/src/commands/reconcile-operation-plan.d.ts +26 -0
  128. package/dist/src/commands/reconcile-operation-plan.js +28 -0
  129. package/dist/src/commands/reconcile-operation-plan.js.map +1 -0
  130. package/dist/src/commands/review-publication.d.ts +99 -0
  131. package/dist/src/commands/review-publication.js +904 -0
  132. package/dist/src/commands/review-publication.js.map +1 -0
  133. package/dist/src/commands/runtime.d.ts +172 -0
  134. package/dist/src/commands/runtime.js +720 -0
  135. package/dist/src/commands/runtime.js.map +1 -0
  136. package/dist/src/commands/skill-eval.d.ts +33 -0
  137. package/dist/src/commands/skill-eval.js +363 -0
  138. package/dist/src/commands/skill-eval.js.map +1 -0
  139. package/dist/src/commands/status-v6.d.ts +206 -0
  140. package/dist/src/commands/status-v6.js +2175 -0
  141. package/dist/src/commands/status-v6.js.map +1 -0
  142. package/dist/src/commands/sync-command.d.ts +23 -0
  143. package/dist/src/commands/sync-command.js +242 -0
  144. package/dist/src/commands/sync-command.js.map +1 -0
  145. package/dist/src/commands/wizard.d.ts +56 -0
  146. package/dist/src/commands/wizard.js +58 -0
  147. package/dist/src/commands/wizard.js.map +1 -0
  148. package/dist/src/commands/write-commands.d.ts +7 -0
  149. package/dist/src/commands/write-commands.js +190 -0
  150. package/dist/src/commands/write-commands.js.map +1 -0
  151. package/dist/src/constants.d.ts +31 -0
  152. package/dist/src/constants.js +32 -0
  153. package/dist/src/constants.js.map +1 -0
  154. package/dist/src/eval/manifest-v1.d.ts +67 -0
  155. package/dist/src/eval/manifest-v1.js +347 -0
  156. package/dist/src/eval/manifest-v1.js.map +1 -0
  157. package/dist/src/generators/agents.d.ts +3 -0
  158. package/dist/src/generators/agents.js +46 -0
  159. package/dist/src/generators/agents.js.map +1 -0
  160. package/dist/src/generators/hooks.d.ts +15 -0
  161. package/dist/src/generators/hooks.js +76 -0
  162. package/dist/src/generators/hooks.js.map +1 -0
  163. package/dist/src/generators/instructions.d.ts +13 -0
  164. package/dist/src/generators/instructions.js +66 -0
  165. package/dist/src/generators/instructions.js.map +1 -0
  166. package/dist/src/generators/local-plugin.d.ts +56 -0
  167. package/dist/src/generators/local-plugin.js +1373 -0
  168. package/dist/src/generators/local-plugin.js.map +1 -0
  169. package/dist/src/generators/permissions.d.ts +10 -0
  170. package/dist/src/generators/permissions.js +37 -0
  171. package/dist/src/generators/permissions.js.map +1 -0
  172. package/dist/src/generators/shared.d.ts +2 -0
  173. package/dist/src/generators/shared.js +15 -0
  174. package/dist/src/generators/shared.js.map +1 -0
  175. package/dist/src/index.d.ts +41 -0
  176. package/dist/src/index.js +3327 -0
  177. package/dist/src/index.js.map +1 -0
  178. package/dist/src/output/command-receipt.d.ts +87 -0
  179. package/dist/src/output/command-receipt.js +302 -0
  180. package/dist/src/output/command-receipt.js.map +1 -0
  181. package/dist/src/output/debug-log.d.ts +4 -0
  182. package/dist/src/output/debug-log.js +97 -0
  183. package/dist/src/output/debug-log.js.map +1 -0
  184. package/dist/src/output/equivalent-command.d.ts +9 -0
  185. package/dist/src/output/equivalent-command.js +39 -0
  186. package/dist/src/output/equivalent-command.js.map +1 -0
  187. package/dist/src/output/model.d.ts +76 -0
  188. package/dist/src/output/model.js +2 -0
  189. package/dist/src/output/model.js.map +1 -0
  190. package/dist/src/output/rebuild-progress.d.ts +21 -0
  191. package/dist/src/output/rebuild-progress.js +128 -0
  192. package/dist/src/output/rebuild-progress.js.map +1 -0
  193. package/dist/src/output/redact.d.ts +3 -0
  194. package/dist/src/output/redact.js +39 -0
  195. package/dist/src/output/redact.js.map +1 -0
  196. package/dist/src/output/reporter.d.ts +9 -0
  197. package/dist/src/output/reporter.js +133 -0
  198. package/dist/src/output/reporter.js.map +1 -0
  199. package/dist/src/portable/commands.d.ts +124 -0
  200. package/dist/src/portable/commands.js +557 -0
  201. package/dist/src/portable/commands.js.map +1 -0
  202. package/dist/src/portable/materializer.d.ts +50 -0
  203. package/dist/src/portable/materializer.js +462 -0
  204. package/dist/src/portable/materializer.js.map +1 -0
  205. package/dist/src/portable/skills-cli.d.ts +43 -0
  206. package/dist/src/portable/skills-cli.js +148 -0
  207. package/dist/src/portable/skills-cli.js.map +1 -0
  208. package/dist/src/tui/catalog-freshness.d.ts +1 -0
  209. package/dist/src/tui/catalog-freshness.js +2 -0
  210. package/dist/src/tui/catalog-freshness.js.map +1 -0
  211. package/dist/src/tui/catalog-generation.d.ts +21 -0
  212. package/dist/src/tui/catalog-generation.js +116 -0
  213. package/dist/src/tui/catalog-generation.js.map +1 -0
  214. package/dist/src/tui/catalog-sources/projection.d.ts +4 -0
  215. package/dist/src/tui/catalog-sources/projection.js +17 -0
  216. package/dist/src/tui/catalog-sources/projection.js.map +1 -0
  217. package/dist/src/tui/catalog-sources/review.d.ts +3 -0
  218. package/dist/src/tui/catalog-sources/review.js +13 -0
  219. package/dist/src/tui/catalog-sources/review.js.map +1 -0
  220. package/dist/src/tui/catalog-sources/screens.d.ts +12 -0
  221. package/dist/src/tui/catalog-sources/screens.js +33 -0
  222. package/dist/src/tui/catalog-sources/screens.js.map +1 -0
  223. package/dist/src/tui/catalog-sources/session.d.ts +22 -0
  224. package/dist/src/tui/catalog-sources/session.js +225 -0
  225. package/dist/src/tui/catalog-sources/session.js.map +1 -0
  226. package/dist/src/tui/catalog-sources/types.d.ts +41 -0
  227. package/dist/src/tui/catalog-sources/types.js +2 -0
  228. package/dist/src/tui/catalog-sources/types.js.map +1 -0
  229. package/dist/src/tui/components.d.ts +102 -0
  230. package/dist/src/tui/components.js +184 -0
  231. package/dist/src/tui/components.js.map +1 -0
  232. package/dist/src/tui/console-app-v4.d.ts +20 -0
  233. package/dist/src/tui/console-app-v4.js +5066 -0
  234. package/dist/src/tui/console-app-v4.js.map +1 -0
  235. package/dist/src/tui/controller-runtime.d.ts +23 -0
  236. package/dist/src/tui/controller-runtime.js +2011 -0
  237. package/dist/src/tui/controller-runtime.js.map +1 -0
  238. package/dist/src/tui/controller.d.ts +557 -0
  239. package/dist/src/tui/controller.js +2120 -0
  240. package/dist/src/tui/controller.js.map +1 -0
  241. package/dist/src/tui/diagnostics.d.ts +30 -0
  242. package/dist/src/tui/diagnostics.js +71 -0
  243. package/dist/src/tui/diagnostics.js.map +1 -0
  244. package/dist/src/tui/index.d.ts +17 -0
  245. package/dist/src/tui/index.js +17 -0
  246. package/dist/src/tui/index.js.map +1 -0
  247. package/dist/src/tui/input-display.d.ts +8 -0
  248. package/dist/src/tui/input-display.js +36 -0
  249. package/dist/src/tui/input-display.js.map +1 -0
  250. package/dist/src/tui/keys.d.ts +12 -0
  251. package/dist/src/tui/keys.js +22 -0
  252. package/dist/src/tui/keys.js.map +1 -0
  253. package/dist/src/tui/layout.d.ts +12 -0
  254. package/dist/src/tui/layout.js +19 -0
  255. package/dist/src/tui/layout.js.map +1 -0
  256. package/dist/src/tui/model.d.ts +424 -0
  257. package/dist/src/tui/model.js +1493 -0
  258. package/dist/src/tui/model.js.map +1 -0
  259. package/dist/src/tui/operation-review-planner.d.ts +21 -0
  260. package/dist/src/tui/operation-review-planner.js +622 -0
  261. package/dist/src/tui/operation-review-planner.js.map +1 -0
  262. package/dist/src/tui/prompt.d.ts +8 -0
  263. package/dist/src/tui/prompt.js +231 -0
  264. package/dist/src/tui/prompt.js.map +1 -0
  265. package/dist/src/tui/router.d.ts +206 -0
  266. package/dist/src/tui/router.js +137 -0
  267. package/dist/src/tui/router.js.map +1 -0
  268. package/dist/src/tui/runner.d.ts +11 -0
  269. package/dist/src/tui/runner.js +71 -0
  270. package/dist/src/tui/runner.js.map +1 -0
  271. package/dist/src/tui/source-ref-presets.d.ts +3 -0
  272. package/dist/src/tui/source-ref-presets.js +11 -0
  273. package/dist/src/tui/source-ref-presets.js.map +1 -0
  274. package/dist/src/tui/theme.d.ts +26 -0
  275. package/dist/src/tui/theme.js +28 -0
  276. package/dist/src/tui/theme.js.map +1 -0
  277. package/dist/src/tui/viewport.d.ts +20 -0
  278. package/dist/src/tui/viewport.js +29 -0
  279. package/dist/src/tui/viewport.js.map +1 -0
  280. package/dist/src/tui/views.d.ts +57 -0
  281. package/dist/src/tui/views.js +201 -0
  282. package/dist/src/tui/views.js.map +1 -0
  283. package/dist/src/tui/wizard.d.ts +11 -0
  284. package/dist/src/tui/wizard.js +323 -0
  285. package/dist/src/tui/wizard.js.map +1 -0
  286. package/dist/src/types.d.ts +474 -0
  287. package/dist/src/types.js +34 -0
  288. package/dist/src/types.js.map +1 -0
  289. package/dist/src/utils/agent-version.d.ts +19 -0
  290. package/dist/src/utils/agent-version.js +205 -0
  291. package/dist/src/utils/agent-version.js.map +1 -0
  292. package/dist/src/utils/artifact-plan.d.ts +27 -0
  293. package/dist/src/utils/artifact-plan.js +453 -0
  294. package/dist/src/utils/artifact-plan.js.map +1 -0
  295. package/dist/src/utils/artifact-renderer.d.ts +129 -0
  296. package/dist/src/utils/artifact-renderer.js +400 -0
  297. package/dist/src/utils/artifact-renderer.js.map +1 -0
  298. package/dist/src/utils/artifact-request.d.ts +16 -0
  299. package/dist/src/utils/artifact-request.js +42 -0
  300. package/dist/src/utils/artifact-request.js.map +1 -0
  301. package/dist/src/utils/artifact-sources.d.ts +8 -0
  302. package/dist/src/utils/artifact-sources.js +10 -0
  303. package/dist/src/utils/artifact-sources.js.map +1 -0
  304. package/dist/src/utils/aw-home-mutation-lock.d.ts +11 -0
  305. package/dist/src/utils/aw-home-mutation-lock.js +83 -0
  306. package/dist/src/utils/aw-home-mutation-lock.js.map +1 -0
  307. package/dist/src/utils/bundle-integrity.d.ts +2 -0
  308. package/dist/src/utils/bundle-integrity.js +63 -0
  309. package/dist/src/utils/bundle-integrity.js.map +1 -0
  310. package/dist/src/utils/bundle-layout.d.ts +4 -0
  311. package/dist/src/utils/bundle-layout.js +531 -0
  312. package/dist/src/utils/bundle-layout.js.map +1 -0
  313. package/dist/src/utils/catalog-digests.d.ts +18 -0
  314. package/dist/src/utils/catalog-digests.js +156 -0
  315. package/dist/src/utils/catalog-digests.js.map +1 -0
  316. package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
  317. package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
  318. package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
  319. package/dist/src/utils/catalog-generation-error.d.ts +16 -0
  320. package/dist/src/utils/catalog-generation-error.js +30 -0
  321. package/dist/src/utils/catalog-generation-error.js.map +1 -0
  322. package/dist/src/utils/catalog-generation-resolver.d.ts +32 -0
  323. package/dist/src/utils/catalog-generation-resolver.js +251 -0
  324. package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
  325. package/dist/src/utils/catalog-generation-service.d.ts +161 -0
  326. package/dist/src/utils/catalog-generation-service.js +1237 -0
  327. package/dist/src/utils/catalog-generation-service.js.map +1 -0
  328. package/dist/src/utils/catalog-generation-state.d.ts +22 -0
  329. package/dist/src/utils/catalog-generation-state.js +192 -0
  330. package/dist/src/utils/catalog-generation-state.js.map +1 -0
  331. package/dist/src/utils/catalog-generation-store.d.ts +57 -0
  332. package/dist/src/utils/catalog-generation-store.js +397 -0
  333. package/dist/src/utils/catalog-generation-store.js.map +1 -0
  334. package/dist/src/utils/catalog-manifest-v7.d.ts +5 -0
  335. package/dist/src/utils/catalog-manifest-v7.js +98 -0
  336. package/dist/src/utils/catalog-manifest-v7.js.map +1 -0
  337. package/dist/src/utils/catalog-operation-context.d.ts +20 -0
  338. package/dist/src/utils/catalog-operation-context.js +141 -0
  339. package/dist/src/utils/catalog-operation-context.js.map +1 -0
  340. package/dist/src/utils/catalog-public-error.d.ts +19 -0
  341. package/dist/src/utils/catalog-public-error.js +100 -0
  342. package/dist/src/utils/catalog-public-error.js.map +1 -0
  343. package/dist/src/utils/catalog-runtime-projection.d.ts +33 -0
  344. package/dist/src/utils/catalog-runtime-projection.js +77 -0
  345. package/dist/src/utils/catalog-runtime-projection.js.map +1 -0
  346. package/dist/src/utils/catalog-snapshot.d.ts +10 -0
  347. package/dist/src/utils/catalog-snapshot.js +20 -0
  348. package/dist/src/utils/catalog-snapshot.js.map +1 -0
  349. package/dist/src/utils/catalog-source-identity.d.ts +5 -0
  350. package/dist/src/utils/catalog-source-identity.js +137 -0
  351. package/dist/src/utils/catalog-source-identity.js.map +1 -0
  352. package/dist/src/utils/catalog-source-impact.d.ts +20 -0
  353. package/dist/src/utils/catalog-source-impact.js +222 -0
  354. package/dist/src/utils/catalog-source-impact.js.map +1 -0
  355. package/dist/src/utils/catalog-source-presentation.d.ts +1 -0
  356. package/dist/src/utils/catalog-source-presentation.js +32 -0
  357. package/dist/src/utils/catalog-source-presentation.js.map +1 -0
  358. package/dist/src/utils/catalog-source-references.d.ts +66 -0
  359. package/dist/src/utils/catalog-source-references.js +1180 -0
  360. package/dist/src/utils/catalog-source-references.js.map +1 -0
  361. package/dist/src/utils/catalog-sources.d.ts +53 -0
  362. package/dist/src/utils/catalog-sources.js +456 -0
  363. package/dist/src/utils/catalog-sources.js.map +1 -0
  364. package/dist/src/utils/codex-config.d.ts +126 -0
  365. package/dist/src/utils/codex-config.js +1032 -0
  366. package/dist/src/utils/codex-config.js.map +1 -0
  367. package/dist/src/utils/codex-manager.d.ts +81 -0
  368. package/dist/src/utils/codex-manager.js +810 -0
  369. package/dist/src/utils/codex-manager.js.map +1 -0
  370. package/dist/src/utils/codex-marketplace-path.d.ts +5 -0
  371. package/dist/src/utils/codex-marketplace-path.js +45 -0
  372. package/dist/src/utils/codex-marketplace-path.js.map +1 -0
  373. package/dist/src/utils/codex-native-observation.d.ts +16 -0
  374. package/dist/src/utils/codex-native-observation.js +22 -0
  375. package/dist/src/utils/codex-native-observation.js.map +1 -0
  376. package/dist/src/utils/codex-native-render-receipt.d.ts +40 -0
  377. package/dist/src/utils/codex-native-render-receipt.js +205 -0
  378. package/dist/src/utils/codex-native-render-receipt.js.map +1 -0
  379. package/dist/src/utils/codex-reconciler.d.ts +128 -0
  380. package/dist/src/utils/codex-reconciler.js +294 -0
  381. package/dist/src/utils/codex-reconciler.js.map +1 -0
  382. package/dist/src/utils/codex-roles.d.ts +71 -0
  383. package/dist/src/utils/codex-roles.js +257 -0
  384. package/dist/src/utils/codex-roles.js.map +1 -0
  385. package/dist/src/utils/codex-shared-native.d.ts +63 -0
  386. package/dist/src/utils/codex-shared-native.js +142 -0
  387. package/dist/src/utils/codex-shared-native.js.map +1 -0
  388. package/dist/src/utils/codex-skill-staging.d.ts +11 -0
  389. package/dist/src/utils/codex-skill-staging.js +8 -0
  390. package/dist/src/utils/codex-skill-staging.js.map +1 -0
  391. package/dist/src/utils/codex-surface-analysis.d.ts +45 -0
  392. package/dist/src/utils/codex-surface-analysis.js +245 -0
  393. package/dist/src/utils/codex-surface-analysis.js.map +1 -0
  394. package/dist/src/utils/config-value.d.ts +3 -0
  395. package/dist/src/utils/config-value.js +23 -0
  396. package/dist/src/utils/config-value.js.map +1 -0
  397. package/dist/src/utils/dynamic-skill-tree.d.ts +36 -0
  398. package/dist/src/utils/dynamic-skill-tree.js +466 -0
  399. package/dist/src/utils/dynamic-skill-tree.js.map +1 -0
  400. package/dist/src/utils/file-rules.d.ts +1 -0
  401. package/dist/src/utils/file-rules.js +2 -0
  402. package/dist/src/utils/file-rules.js.map +1 -0
  403. package/dist/src/utils/git-catalog-provider.d.ts +22 -0
  404. package/dist/src/utils/git-catalog-provider.js +497 -0
  405. package/dist/src/utils/git-catalog-provider.js.map +1 -0
  406. package/dist/src/utils/hooks-contract.d.ts +1 -0
  407. package/dist/src/utils/hooks-contract.js +2 -0
  408. package/dist/src/utils/hooks-contract.js.map +1 -0
  409. package/dist/src/utils/json.d.ts +4 -0
  410. package/dist/src/utils/json.js +29 -0
  411. package/dist/src/utils/json.js.map +1 -0
  412. package/dist/src/utils/local-marketplace.d.ts +41 -0
  413. package/dist/src/utils/local-marketplace.js +316 -0
  414. package/dist/src/utils/local-marketplace.js.map +1 -0
  415. package/dist/src/utils/manifest.d.ts +11 -0
  416. package/dist/src/utils/manifest.js +33 -0
  417. package/dist/src/utils/manifest.js.map +1 -0
  418. package/dist/src/utils/marketplace.d.ts +3 -0
  419. package/dist/src/utils/marketplace.js +76 -0
  420. package/dist/src/utils/marketplace.js.map +1 -0
  421. package/dist/src/utils/multi-source-catalog.d.ts +66 -0
  422. package/dist/src/utils/multi-source-catalog.js +345 -0
  423. package/dist/src/utils/multi-source-catalog.js.map +1 -0
  424. package/dist/src/utils/native-cli.d.ts +118 -0
  425. package/dist/src/utils/native-cli.js +518 -0
  426. package/dist/src/utils/native-cli.js.map +1 -0
  427. package/dist/src/utils/orphan-marketplace-cleanup.d.ts +18 -0
  428. package/dist/src/utils/orphan-marketplace-cleanup.js +198 -0
  429. package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -0
  430. package/dist/src/utils/ownership.d.ts +194 -0
  431. package/dist/src/utils/ownership.js +723 -0
  432. package/dist/src/utils/ownership.js.map +1 -0
  433. package/dist/src/utils/path-catalog-provider.d.ts +7 -0
  434. package/dist/src/utils/path-catalog-provider.js +74 -0
  435. package/dist/src/utils/path-catalog-provider.js.map +1 -0
  436. package/dist/src/utils/paths.d.ts +40 -0
  437. package/dist/src/utils/paths.js +137 -0
  438. package/dist/src/utils/paths.js.map +1 -0
  439. package/dist/src/utils/platform-entries.d.ts +1 -0
  440. package/dist/src/utils/platform-entries.js +2 -0
  441. package/dist/src/utils/platform-entries.js.map +1 -0
  442. package/dist/src/utils/platform-support.d.ts +1 -0
  443. package/dist/src/utils/platform-support.js +2 -0
  444. package/dist/src/utils/platform-support.js.map +1 -0
  445. package/dist/src/utils/plugin-config-write.d.ts +110 -0
  446. package/dist/src/utils/plugin-config-write.js +773 -0
  447. package/dist/src/utils/plugin-config-write.js.map +1 -0
  448. package/dist/src/utils/plugin-config.d.ts +389 -0
  449. package/dist/src/utils/plugin-config.js +1642 -0
  450. package/dist/src/utils/plugin-config.js.map +1 -0
  451. package/dist/src/utils/plugin-dependencies.d.ts +12 -0
  452. package/dist/src/utils/plugin-dependencies.js +162 -0
  453. package/dist/src/utils/plugin-dependencies.js.map +1 -0
  454. package/dist/src/utils/plugin-intent-state.d.ts +34 -0
  455. package/dist/src/utils/plugin-intent-state.js +289 -0
  456. package/dist/src/utils/plugin-intent-state.js.map +1 -0
  457. package/dist/src/utils/plugin-script-contract.d.ts +1 -0
  458. package/dist/src/utils/plugin-script-contract.js +2 -0
  459. package/dist/src/utils/plugin-script-contract.js.map +1 -0
  460. package/dist/src/utils/plugin-scripts.d.ts +101 -0
  461. package/dist/src/utils/plugin-scripts.js +253 -0
  462. package/dist/src/utils/plugin-scripts.js.map +1 -0
  463. package/dist/src/utils/process-lock.d.ts +47 -0
  464. package/dist/src/utils/process-lock.js +532 -0
  465. package/dist/src/utils/process-lock.js.map +1 -0
  466. package/dist/src/utils/process-tree.d.ts +2 -0
  467. package/dist/src/utils/process-tree.js +2 -0
  468. package/dist/src/utils/process-tree.js.map +1 -0
  469. package/dist/src/utils/project-boundary.d.ts +9 -0
  470. package/dist/src/utils/project-boundary.js +67 -0
  471. package/dist/src/utils/project-boundary.js.map +1 -0
  472. package/dist/src/utils/rendered-contract.d.ts +8 -0
  473. package/dist/src/utils/rendered-contract.js +134 -0
  474. package/dist/src/utils/rendered-contract.js.map +1 -0
  475. package/dist/src/utils/runtime-skill-closure.d.ts +13 -0
  476. package/dist/src/utils/runtime-skill-closure.js +863 -0
  477. package/dist/src/utils/runtime-skill-closure.js.map +1 -0
  478. package/dist/src/utils/safe-fs.d.ts +21 -0
  479. package/dist/src/utils/safe-fs.js +111 -0
  480. package/dist/src/utils/safe-fs.js.map +1 -0
  481. package/dist/src/utils/skill-authoring.d.ts +1 -0
  482. package/dist/src/utils/skill-authoring.js +2 -0
  483. package/dist/src/utils/skill-authoring.js.map +1 -0
  484. package/dist/src/utils/skill-validation.d.ts +37 -0
  485. package/dist/src/utils/skill-validation.js +326 -0
  486. package/dist/src/utils/skill-validation.js.map +1 -0
  487. package/dist/src/utils/slash-commands.d.ts +1 -0
  488. package/dist/src/utils/slash-commands.js +2 -0
  489. package/dist/src/utils/slash-commands.js.map +1 -0
  490. package/dist/src/utils/state-paths.d.ts +14 -0
  491. package/dist/src/utils/state-paths.js +139 -0
  492. package/dist/src/utils/state-paths.js.map +1 -0
  493. package/dist/src/utils/target-eligibility.d.ts +13 -0
  494. package/dist/src/utils/target-eligibility.js +35 -0
  495. package/dist/src/utils/target-eligibility.js.map +1 -0
  496. package/dist/src/utils/target-order.d.ts +7 -0
  497. package/dist/src/utils/target-order.js +10 -0
  498. package/dist/src/utils/target-order.js.map +1 -0
  499. package/dist/src/utils/template-engine.d.ts +1 -0
  500. package/dist/src/utils/template-engine.js +2 -0
  501. package/dist/src/utils/template-engine.js.map +1 -0
  502. package/dist/src/utils/test-path-fence.d.ts +12 -0
  503. package/dist/src/utils/test-path-fence.js +31 -0
  504. package/dist/src/utils/test-path-fence.js.map +1 -0
  505. package/dist/vitest.config.d.ts +2 -0
  506. package/dist/vitest.config.js +16 -0
  507. package/dist/vitest.config.js.map +1 -0
  508. package/package.json +64 -0
@@ -0,0 +1,1642 @@
1
+ import crypto from 'node:crypto';
2
+ import * as nodeFs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import fs from 'fs-extra';
5
+ import { canonicalizePluginConfigValues, formatPluginConfigIssue, matchPluginConfigPreset, redactPluginConfigValue, stablePluginConfigStringify, validatePluginConfigDocument, validatePluginConfigValue as validateCorePluginConfigValue, } from '@lesliechan721/aw-plugin-core/manifest';
6
+ import { assertTestReadablePath, assertTestWritablePath, } from './test-path-fence.js';
7
+ import { canonicalizePathThroughExistingAncestor, resolveAwHome, resolveRepoRootSync, } from './paths.js';
8
+ import { assertRealFileParentWithinRoot, assertSafeFileMutationPath, atomicReplaceSafeFile, } from './safe-fs.js';
9
+ export function codexNativeRefreshKeys(manifest) {
10
+ return new Set((manifest.artifacts?.targets ?? []).flatMap((target) => target.platform === 'codex'
11
+ && target.target === 'codex.skill'
12
+ && target.delivery?.mode === 'codex-native'
13
+ ? target.refreshKeys
14
+ : []));
15
+ }
16
+ export class PluginConfigResetError extends Error {
17
+ code;
18
+ osCode;
19
+ constructor(code, message, osCode) {
20
+ super(`${code}: ${message}`);
21
+ this.name = 'PluginConfigResetError';
22
+ this.code = code;
23
+ this.osCode = osCode;
24
+ }
25
+ }
26
+ export class PluginConfigSchemaResolutionError extends Error {
27
+ code;
28
+ details;
29
+ constructor(code, message, details) {
30
+ super(`${code}: ${message}`);
31
+ this.name = 'PluginConfigSchemaResolutionError';
32
+ this.code = code;
33
+ this.details = details;
34
+ }
35
+ }
36
+ export function isPluginConfigSchemaResolutionError(error) {
37
+ return error instanceof PluginConfigSchemaResolutionError;
38
+ }
39
+ export class PluginConfigStoreInventoryError extends Error {
40
+ code = 'invalidPluginConfigStore';
41
+ constructor(message) {
42
+ super(`invalidPluginConfigStore: ${message}`);
43
+ this.name = 'PluginConfigStoreInventoryError';
44
+ }
45
+ }
46
+ const CONFIG_DIR = 'plugin-config';
47
+ const CONFIG_FILE = 'config.json';
48
+ let atomicConfigBeforeRenameHookForTests;
49
+ let atomicConfigAfterRenameHookForTests;
50
+ export function setAtomicConfigBeforeRenameHookForTests(hook) {
51
+ atomicConfigBeforeRenameHookForTests = hook;
52
+ }
53
+ export function setAtomicConfigAfterRenameHookForTests(hook) {
54
+ atomicConfigAfterRenameHookForTests = hook;
55
+ }
56
+ function assertConfigPathBoundary(allowedRoot, candidatePath) {
57
+ const root = path.resolve(allowedRoot);
58
+ const candidate = path.resolve(candidatePath);
59
+ const relative = path.relative(root, candidate);
60
+ if (relative === ''
61
+ || relative === '..'
62
+ || relative.startsWith(`..${path.sep}`)
63
+ || path.isAbsolute(relative)) {
64
+ throw new Error(`Config path escapes its allowed root: ${candidate}`);
65
+ }
66
+ return { root, candidate };
67
+ }
68
+ async function lstatIfExists(targetPath) {
69
+ try {
70
+ return await fs.lstat(targetPath);
71
+ }
72
+ catch (error) {
73
+ if (error.code === 'ENOENT')
74
+ return null;
75
+ throw error;
76
+ }
77
+ }
78
+ async function assertNoConfigSymlink(allowedRoot, candidatePath) {
79
+ const { root, candidate } = assertConfigPathBoundary(allowedRoot, candidatePath);
80
+ const paths = [root];
81
+ let current = root;
82
+ for (const segment of path.relative(root, candidate).split(path.sep)) {
83
+ current = path.join(current, segment);
84
+ paths.push(current);
85
+ }
86
+ for (const currentPath of paths) {
87
+ const stats = await lstatIfExists(currentPath);
88
+ if (stats?.isSymbolicLink()) {
89
+ throw new Error(`Config path cannot contain a symbolic link: ${currentPath}`);
90
+ }
91
+ }
92
+ }
93
+ async function inspectConfigReadTarget(allowedRoot, filePath) {
94
+ await assertNoConfigSymlink(allowedRoot, filePath);
95
+ const stats = await lstatIfExists(filePath);
96
+ if (!stats)
97
+ return null;
98
+ if (!stats.isFile() || stats.isSymbolicLink()) {
99
+ throw new Error(`Config read target must be a regular file: ${filePath}`);
100
+ }
101
+ return stats;
102
+ }
103
+ async function inspectConfigInventoryStoreRoot(boundaryRoot, storeRoot) {
104
+ const { root, candidate } = assertConfigPathBoundary(boundaryRoot, path.join(storeRoot, CONFIG_FILE));
105
+ let current = root;
106
+ for (const segment of path.relative(root, path.dirname(candidate)).split(path.sep).filter(Boolean)) {
107
+ current = path.join(current, segment);
108
+ const stats = await lstatIfExists(current);
109
+ if (!stats)
110
+ return false;
111
+ if (stats.isSymbolicLink() || !stats.isDirectory()) {
112
+ throw new PluginConfigStoreInventoryError(stats.isSymbolicLink()
113
+ ? `Config path cannot contain a symbolic link: ${current}`
114
+ : `Config inventory boundary must be a directory: ${current}`);
115
+ }
116
+ }
117
+ return true;
118
+ }
119
+ async function prepareConfigFileParent(filePath, allowedRoot) {
120
+ assertConfigPathBoundary(allowedRoot, filePath);
121
+ await assertNoConfigSymlink(allowedRoot, filePath);
122
+ await fs.ensureDir(path.dirname(filePath));
123
+ await assertNoConfigSymlink(allowedRoot, filePath);
124
+ }
125
+ export async function withConfigFileMutation(options) {
126
+ await prepareConfigFileParent(options.filePath, options.allowedRoot);
127
+ await assertNoConfigSymlink(options.allowedRoot, options.filePath);
128
+ return options.action();
129
+ }
130
+ export async function atomicReplaceConfigFile(options) {
131
+ await atomicReplaceSafeFile({
132
+ ...options,
133
+ label: 'Config path',
134
+ temporaryTag: 'tmp',
135
+ beforeRename: atomicConfigBeforeRenameHookForTests,
136
+ afterRename: async (targetPath) => {
137
+ await atomicConfigAfterRenameHookForTests?.(targetPath).catch(() => undefined);
138
+ },
139
+ });
140
+ }
141
+ export async function atomicRemoveConfigFile(options) {
142
+ await prepareConfigFileParent(options.filePath, options.allowedRoot);
143
+ await assertSafeFileMutationPath({
144
+ filePath: options.filePath,
145
+ allowedRoot: options.allowedRoot,
146
+ label: 'Config path',
147
+ });
148
+ await assertRealFileParentWithinRoot({
149
+ filePath: options.filePath,
150
+ allowedRoot: options.allowedRoot,
151
+ label: 'Config path',
152
+ });
153
+ const currentTarget = await lstatIfExists(options.filePath);
154
+ if (!currentTarget)
155
+ return false;
156
+ if (!currentTarget.isFile() || currentTarget.isSymbolicLink()) {
157
+ throw new Error(`Config target must be a regular file: ${options.filePath}`);
158
+ }
159
+ await atomicConfigBeforeRenameHookForTests?.(options.filePath, options.filePath);
160
+ await assertSafeFileMutationPath({
161
+ filePath: options.filePath,
162
+ allowedRoot: options.allowedRoot,
163
+ label: 'Config path',
164
+ });
165
+ await assertRealFileParentWithinRoot({
166
+ filePath: options.filePath,
167
+ allowedRoot: options.allowedRoot,
168
+ label: 'Config path',
169
+ });
170
+ const targetBeforeRemove = await lstatIfExists(options.filePath);
171
+ if (!targetBeforeRemove)
172
+ return false;
173
+ if (!targetBeforeRemove.isFile() || targetBeforeRemove.isSymbolicLink()) {
174
+ throw new Error(`Config target must be a regular file: ${options.filePath}`);
175
+ }
176
+ await nodeFs.unlink(options.filePath);
177
+ await atomicConfigAfterRenameHookForTests?.(options.filePath).catch(() => undefined);
178
+ return true;
179
+ }
180
+ function isRecord(value) {
181
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
182
+ }
183
+ function formatValue(value) {
184
+ if (typeof value === 'string')
185
+ return value;
186
+ if (typeof value === 'number' || typeof value === 'boolean') {
187
+ return stablePluginConfigStringify(value);
188
+ }
189
+ if (isRecord(value)) {
190
+ return stablePluginConfigStringify(value);
191
+ }
192
+ return JSON.stringify(value);
193
+ }
194
+ async function calculateConfigManifestDigest(filePath) {
195
+ const hash = crypto.createHash('sha256');
196
+ hash.update(await fs.readFile(filePath));
197
+ return `sha256:${hash.digest('hex')}`;
198
+ }
199
+ async function calculateConfigSchemaManifestDigestForPlugin(plugin) {
200
+ if (await lstatIfExists(plugin.manifestPath)) {
201
+ return calculateConfigManifestDigest(plugin.manifestPath);
202
+ }
203
+ const hash = crypto.createHash('sha256');
204
+ hash.update(JSON.stringify(plugin.manifest));
205
+ return `sha256:${hash.digest('hex')}`;
206
+ }
207
+ function createConfigSchemaEvidenceFromCurrentCatalog(plugin, manifestDigest) {
208
+ return {
209
+ source: 'currentCatalog',
210
+ identity: createPluginConfigSchemaIdentity(plugin.manifest),
211
+ pluginVersion: plugin.manifest.version,
212
+ manifestDigest,
213
+ };
214
+ }
215
+ function getStoreEntries(values) {
216
+ return Object.fromEntries(Object.entries(values));
217
+ }
218
+ export function assertSafePluginConfigSegment(pluginName) {
219
+ if (typeof pluginName !== 'string' || pluginName.trim() === '') {
220
+ throw new Error('Invalid plugin config segment: expected a non-empty plugin name.');
221
+ }
222
+ if (pluginName !== pluginName.trim() ||
223
+ pluginName === '.' ||
224
+ pluginName === '..' ||
225
+ pluginName.includes('/') ||
226
+ pluginName.includes('\\') ||
227
+ path.posix.isAbsolute(pluginName) ||
228
+ path.win32.isAbsolute(pluginName) ||
229
+ /^[A-Za-z][A-Za-z0-9+.-]*:/.test(pluginName)) {
230
+ throw new Error(`Invalid plugin config segment "${pluginName}".`);
231
+ }
232
+ return pluginName;
233
+ }
234
+ export function resolveAwProjectRoot(cwd) {
235
+ return path.resolve(resolveRepoRootSync(cwd));
236
+ }
237
+ function resolvePluginConfigAllowedRoot(options) {
238
+ return options.scope === 'user'
239
+ ? resolveAwHome(options.env)
240
+ : path.join(resolveAwProjectRoot(options.cwd), '.agent-workflows');
241
+ }
242
+ export function resolvePluginConfigStorePath(options) {
243
+ const pluginName = assertSafePluginConfigSegment(options.pluginName);
244
+ const sourceId = options.sourceId === undefined
245
+ ? undefined
246
+ : assertSafePluginConfigSegment(options.sourceId);
247
+ const root = options.scope === 'user'
248
+ ? resolveAwHome(options.env)
249
+ : path.join(resolveAwProjectRoot(options.cwd), '.agent-workflows');
250
+ const filePath = path.join(root, CONFIG_DIR, ...(sourceId ? [sourceId] : []), pluginName, CONFIG_FILE);
251
+ if (options.scope !== 'project') {
252
+ return filePath;
253
+ }
254
+ if (options.access === 'read') {
255
+ return assertTestReadablePath(filePath, 'project plugin config store (.agent-workflows/plugin-config)', '用 registerReadonlyFixtureRoot() 或 runWithRepositoryCwdAsReadOnly() 登记只读 project fixture 根;需要写入时改用 withSafeTestCwd() 或 registerWritableStateRoot()。');
256
+ }
257
+ if (options.access === 'write') {
258
+ return assertTestWritablePath(filePath, 'project plugin config store (.agent-workflows/plugin-config)', '用 withSafeTestCwd() 切到测试可写 cwd,或通过 registerWritableStateRoot() 登记 project fixture 根。');
259
+ }
260
+ return filePath;
261
+ }
262
+ export function resolvePluginConfigStoreTargets(options) {
263
+ if (options.scope === 'user') {
264
+ return [{
265
+ storeKind: 'user',
266
+ path: resolvePluginConfigStorePath(options),
267
+ }];
268
+ }
269
+ return [{
270
+ storeKind: 'project',
271
+ path: resolvePluginConfigStorePath(options),
272
+ }];
273
+ }
274
+ function isPluginConfigValue(value) {
275
+ if (typeof value === 'string' || typeof value === 'boolean')
276
+ return true;
277
+ if (typeof value === 'number')
278
+ return Number.isFinite(value);
279
+ if (!isRecord(value))
280
+ return false;
281
+ return Object.values(value).every((child) => typeof child === 'string'
282
+ || typeof child === 'boolean'
283
+ || (typeof child === 'number' && Number.isFinite(child)));
284
+ }
285
+ function invalidStore(filePath, code, message, rawDigest) {
286
+ return {
287
+ path: filePath,
288
+ exists: true,
289
+ state: 'invalid',
290
+ values: {},
291
+ ...(rawDigest ? { rawDigest } : {}),
292
+ invalidCode: code,
293
+ issues: [message],
294
+ };
295
+ }
296
+ function readyStore(filePath, values, rawDigest) {
297
+ return {
298
+ path: filePath,
299
+ exists: true,
300
+ state: 'ready',
301
+ values: canonicalizePluginConfigValues(values),
302
+ ...(rawDigest ? { rawDigest } : {}),
303
+ issues: [],
304
+ };
305
+ }
306
+ export function assertReadyPluginConfigStore(store) {
307
+ if (store.state !== 'invalid')
308
+ return;
309
+ throw new Error(`${store.invalidCode}: Invalid plugin config store "${store.path}". ${store.issues.join(' ')}`);
310
+ }
311
+ async function parseStoreJson(filePath) {
312
+ const content = await fs.readFile(filePath);
313
+ const rawDigest = `sha256:${crypto.createHash('sha256').update(content).digest('hex')}`;
314
+ try {
315
+ return {
316
+ state: 'parsed',
317
+ value: JSON.parse(content.toString('utf8')),
318
+ rawDigest,
319
+ };
320
+ }
321
+ catch (error) {
322
+ if (error instanceof SyntaxError) {
323
+ return {
324
+ state: 'invalidJson',
325
+ store: invalidStore(filePath, 'invalidJson', 'The store is not valid JSON.', rawDigest),
326
+ };
327
+ }
328
+ throw error;
329
+ }
330
+ }
331
+ function validateStoreValues(options) {
332
+ if (!isRecord(options.values)) {
333
+ return invalidStore(options.filePath, 'invalidValues', 'The values field must be an object.', options.rawDigest);
334
+ }
335
+ const values = Object.create(null);
336
+ const issues = [];
337
+ for (const [key, value] of Object.entries(options.values)) {
338
+ if (!isPluginConfigValue(value)) {
339
+ issues.push(`Config "${key}" is not a supported config value.`);
340
+ continue;
341
+ }
342
+ values[key] = value;
343
+ }
344
+ return issues.length > 0
345
+ ? invalidStore(options.filePath, 'invalidValues', issues.join(' '), options.rawDigest)
346
+ : readyStore(options.filePath, values, options.rawDigest);
347
+ }
348
+ async function readStoreFile(filePath) {
349
+ const parsed = await parseStoreJson(filePath);
350
+ if (parsed.state === 'invalidJson')
351
+ return parsed.store;
352
+ return validateStoreValues({ filePath, values: parsed.value, rawDigest: parsed.rawDigest });
353
+ }
354
+ async function readNamespacedStoreFile(options) {
355
+ const parsed = await parseStoreJson(options.filePath);
356
+ if (parsed.state === 'invalidJson')
357
+ return parsed.store;
358
+ const raw = parsed.value;
359
+ if (!isRecord(raw) || raw.schemaVersion !== 3) {
360
+ return invalidStore(options.filePath, 'schemaVersionUnsupported', 'Only plugin config store schemaVersion 3 is supported.', parsed.rawDigest);
361
+ }
362
+ if (raw.sourceId !== options.sourceId
363
+ || raw.pluginName !== options.pluginName
364
+ || raw.sourceIdentity !== options.sourceIdentity) {
365
+ return invalidStore(options.filePath, 'identityMismatch', 'The store identity does not match its source and namespaced path.', parsed.rawDigest);
366
+ }
367
+ return validateStoreValues({
368
+ filePath: options.filePath,
369
+ values: raw.values,
370
+ rawDigest: parsed.rawDigest,
371
+ });
372
+ }
373
+ function namespacedConfigContent(options) {
374
+ const values = canonicalizePluginConfigValues(options.values);
375
+ if (!options.sourceId)
376
+ return `${JSON.stringify(values, null, 2)}\n`;
377
+ if (!options.sourceIdentity) {
378
+ throw new Error('sourceChanged: namespaced plugin config requires a source identity.');
379
+ }
380
+ const envelope = {
381
+ schemaVersion: 3,
382
+ sourceId: options.sourceId,
383
+ sourceIdentity: options.sourceIdentity,
384
+ pluginName: options.pluginName,
385
+ values,
386
+ };
387
+ return `${JSON.stringify(envelope, null, 2)}\n`;
388
+ }
389
+ async function assertNoLegacyConfigCoexistence(options) {
390
+ if (!options.sourceId)
391
+ return;
392
+ const legacyPath = resolvePluginConfigStorePath({
393
+ pluginName: options.pluginName,
394
+ scope: options.scope,
395
+ cwd: options.cwd,
396
+ env: options.env,
397
+ ...(options.scope === 'project' ? { access: 'read' } : {}),
398
+ });
399
+ if (await lstatIfExists(legacyPath)) {
400
+ throw new Error('legacyStateUnsupported: legacy and namespaced plugin config stores cannot coexist.');
401
+ }
402
+ }
403
+ export async function readPluginConfigStore(options) {
404
+ const filePath = resolvePluginConfigStorePath({
405
+ ...options,
406
+ ...(options.scope === 'project' ? { access: 'read' } : {}),
407
+ });
408
+ const allowedRoot = resolvePluginConfigAllowedRoot(options);
409
+ if (options.sourceId) {
410
+ const legacyPath = resolvePluginConfigStorePath({
411
+ pluginName: options.pluginName,
412
+ scope: options.scope,
413
+ cwd: options.cwd,
414
+ env: options.env,
415
+ ...(options.scope === 'project' ? { access: 'read' } : {}),
416
+ });
417
+ if (await inspectConfigReadTarget(allowedRoot, legacyPath)) {
418
+ throw new Error('legacyStateUnsupported: legacy and namespaced plugin config stores cannot coexist.');
419
+ }
420
+ }
421
+ if (!await inspectConfigReadTarget(allowedRoot, filePath)) {
422
+ return {
423
+ path: filePath,
424
+ exists: false,
425
+ state: 'missing',
426
+ values: {},
427
+ issues: [],
428
+ };
429
+ }
430
+ return options.sourceId
431
+ ? readNamespacedStoreFile({
432
+ filePath,
433
+ sourceId: options.sourceId,
434
+ sourceIdentity: options.sourceIdentity,
435
+ pluginName: options.pluginName,
436
+ })
437
+ : readStoreFile(filePath);
438
+ }
439
+ export async function readNamespacedPluginConfigStoreIdentity(options) {
440
+ const filePath = resolvePluginConfigStorePath({
441
+ ...options,
442
+ ...(options.scope === 'project' ? { access: 'read' } : {}),
443
+ });
444
+ if (!await inspectConfigReadTarget(resolvePluginConfigAllowedRoot(options), filePath))
445
+ return null;
446
+ const parsed = await parseStoreJson(filePath);
447
+ if (parsed.state === 'invalidJson') {
448
+ throw new Error(`${parsed.store.invalidCode}: invalid namespaced plugin config store.`);
449
+ }
450
+ const raw = parsed.value;
451
+ if (!isRecord(raw) || raw.schemaVersion !== 3 || !isRecord(raw.values)
452
+ || raw.sourceId !== options.sourceId || raw.pluginName !== options.pluginName
453
+ || typeof raw.sourceIdentity !== 'string') {
454
+ throw new Error('schemaVersionUnsupported: invalid namespaced plugin config store.');
455
+ }
456
+ const store = validateStoreValues({ filePath, values: raw.values });
457
+ assertReadyPluginConfigStore(store);
458
+ return {
459
+ sourceIdentity: raw.sourceIdentity,
460
+ store,
461
+ };
462
+ }
463
+ async function listConfigStoreDirectoryPluginNames(storeRoot) {
464
+ if (!await lstatIfExists(storeRoot)) {
465
+ return [];
466
+ }
467
+ const entries = await fs.readdir(storeRoot, { withFileTypes: true });
468
+ const pluginNames = [];
469
+ for (const entry of entries) {
470
+ if (!entry.isDirectory()) {
471
+ throw new PluginConfigStoreInventoryError(`Config plugin namespace must be a directory: ${path.join(storeRoot, entry.name)}`);
472
+ }
473
+ let pluginName;
474
+ try {
475
+ pluginName = assertSafePluginConfigSegment(entry.name);
476
+ }
477
+ catch {
478
+ continue;
479
+ }
480
+ if (await lstatIfExists(path.join(storeRoot, pluginName, CONFIG_FILE))) {
481
+ pluginNames.push(pluginName);
482
+ }
483
+ }
484
+ return pluginNames;
485
+ }
486
+ export async function listPluginConfigStorePluginNames(options) {
487
+ const projectRoot = resolveAwProjectRoot(options.cwd);
488
+ const storeRoots = options.scope === 'user'
489
+ ? [path.join(resolveAwHome(options.env), CONFIG_DIR)]
490
+ : options.scope === 'project'
491
+ ? [path.join(projectRoot, '.agent-workflows', CONFIG_DIR)]
492
+ : [
493
+ path.join(resolveAwHome(options.env), CONFIG_DIR),
494
+ path.join(projectRoot, '.agent-workflows', CONFIG_DIR),
495
+ ];
496
+ const pluginNames = (await Promise.all(storeRoots.map((storeRoot) => listConfigStoreDirectoryPluginNames(storeRoot)))).flat();
497
+ return [...new Set(pluginNames)].sort((left, right) => left.localeCompare(right));
498
+ }
499
+ export async function listNamespacedPluginConfigStores(options) {
500
+ const boundaryRoot = options.scope === 'user'
501
+ ? resolveAwHome(options.env)
502
+ : resolveAwProjectRoot(options.cwd);
503
+ if (options.scope === 'user') {
504
+ await canonicalizePathThroughExistingAncestor(boundaryRoot);
505
+ }
506
+ else {
507
+ await fs.realpath(boundaryRoot);
508
+ }
509
+ const storeRoot = path.join(boundaryRoot, ...(options.scope === 'project' ? ['.agent-workflows'] : []), CONFIG_DIR);
510
+ const output = [];
511
+ if (!await inspectConfigInventoryStoreRoot(boundaryRoot, storeRoot))
512
+ return output;
513
+ for (const sourceEntry of await fs.readdir(storeRoot, { withFileTypes: true })) {
514
+ if (!sourceEntry.isDirectory()) {
515
+ throw new PluginConfigStoreInventoryError(sourceEntry.isSymbolicLink()
516
+ ? `Config path cannot contain a symbolic link: ${path.join(storeRoot, sourceEntry.name)}`
517
+ : `Config source namespace must be a directory: ${path.join(storeRoot, sourceEntry.name)}`);
518
+ }
519
+ const sourceId = assertSafePluginConfigSegment(sourceEntry.name);
520
+ const sourceRoot = path.join(storeRoot, sourceId);
521
+ for (const pluginEntry of await fs.readdir(sourceRoot, { withFileTypes: true })) {
522
+ if (!pluginEntry.isDirectory()) {
523
+ throw new PluginConfigStoreInventoryError(pluginEntry.isSymbolicLink()
524
+ ? `Config path cannot contain a symbolic link: ${path.join(sourceRoot, pluginEntry.name)}`
525
+ : `Config plugin namespace must be a directory: ${path.join(sourceRoot, pluginEntry.name)}`);
526
+ }
527
+ const pluginName = assertSafePluginConfigSegment(pluginEntry.name);
528
+ const filePath = path.join(storeRoot, sourceId, pluginName, CONFIG_FILE);
529
+ if (!await inspectConfigReadTarget(storeRoot, filePath))
530
+ continue;
531
+ const parsed = await parseStoreJson(filePath);
532
+ if (parsed.state === 'invalidJson') {
533
+ throw new Error(`${parsed.store.invalidCode}: invalid namespaced plugin config store "${filePath}".`);
534
+ }
535
+ const raw = parsed.value;
536
+ if (!isRecord(raw) || raw.schemaVersion !== 3 || !isRecord(raw.values)
537
+ || raw.sourceId !== sourceId || raw.pluginName !== pluginName
538
+ || typeof raw.sourceIdentity !== 'string') {
539
+ throw new Error(`schemaVersionUnsupported: invalid namespaced plugin config store "${filePath}".`);
540
+ }
541
+ const store = validateStoreValues({ filePath, values: raw.values });
542
+ assertReadyPluginConfigStore(store);
543
+ output.push({
544
+ sourceId,
545
+ sourceIdentity: raw.sourceIdentity,
546
+ pluginName,
547
+ pluginId: `${sourceId}/${pluginName}`,
548
+ values: store.values,
549
+ });
550
+ }
551
+ }
552
+ return output.sort((left, right) => left.pluginId.localeCompare(right.pluginId));
553
+ }
554
+ export async function writePluginConfigStore(options) {
555
+ await assertNoLegacyConfigCoexistence(options);
556
+ const filePath = resolvePluginConfigStorePath({
557
+ ...options,
558
+ ...(options.scope === 'project' ? { access: 'write' } : {}),
559
+ });
560
+ const allowedRoot = resolvePluginConfigAllowedRoot(options);
561
+ const validated = validateStoreValues({ filePath, values: options.values });
562
+ assertReadyPluginConfigStore(validated);
563
+ await withConfigFileMutation({
564
+ filePath,
565
+ allowedRoot,
566
+ action: async () => Object.keys(validated.values).length === 0
567
+ ? atomicRemoveConfigFile({ filePath, allowedRoot })
568
+ : atomicReplaceConfigFile({
569
+ filePath,
570
+ allowedRoot,
571
+ content: namespacedConfigContent({ ...options, values: validated.values }),
572
+ mode: 0o600,
573
+ }),
574
+ });
575
+ return {
576
+ pluginName: options.pluginName,
577
+ scope: options.scope,
578
+ path: filePath,
579
+ };
580
+ }
581
+ export async function patchPluginConfigStore(options) {
582
+ await assertNoLegacyConfigCoexistence(options);
583
+ const filePath = resolvePluginConfigStorePath({
584
+ ...options,
585
+ ...(options.scope === 'project' ? { access: 'write' } : {}),
586
+ });
587
+ const allowedRoot = resolvePluginConfigAllowedRoot(options);
588
+ const removedKeys = options.removeKeys ?? [];
589
+ await withConfigFileMutation({
590
+ filePath,
591
+ allowedRoot,
592
+ action: async () => {
593
+ await options.reviewPreflight?.();
594
+ const current = (await lstatIfExists(filePath))
595
+ ? options.sourceId
596
+ ? await readNamespacedStoreFile({
597
+ filePath,
598
+ sourceId: options.sourceId,
599
+ sourceIdentity: options.sourceIdentity,
600
+ pluginName: options.pluginName,
601
+ })
602
+ : await readStoreFile(filePath)
603
+ : readyStore(filePath, {});
604
+ assertReadyPluginConfigStore(current);
605
+ if (options.requireExistingRemoveKeys) {
606
+ const missingKey = removedKeys.find((key) => !Object.hasOwn(current.values, key));
607
+ if (missingKey)
608
+ throw new Error(`Unknown config key "${missingKey}".`);
609
+ }
610
+ const next = { ...current.values };
611
+ for (const key of removedKeys) {
612
+ delete next[key];
613
+ }
614
+ for (const [key, value] of Object.entries(options.updates)) {
615
+ next[key] = value;
616
+ }
617
+ if (Object.keys(next).length === 0) {
618
+ await atomicRemoveConfigFile({ filePath, allowedRoot });
619
+ }
620
+ else {
621
+ await atomicReplaceConfigFile({
622
+ filePath,
623
+ allowedRoot,
624
+ content: namespacedConfigContent({ ...options, values: next }),
625
+ mode: 0o600,
626
+ });
627
+ }
628
+ },
629
+ });
630
+ return {
631
+ pluginName: options.pluginName,
632
+ scope: options.scope,
633
+ path: filePath,
634
+ updatedKeys: Object.keys(options.updates),
635
+ removedKeys,
636
+ };
637
+ }
638
+ export async function resetPluginConfigStore(options) {
639
+ const filePath = resolvePluginConfigStorePath({
640
+ ...options,
641
+ ...(options.scope === 'project' ? { access: 'write' } : {}),
642
+ });
643
+ const allowedRoot = resolvePluginConfigAllowedRoot(options);
644
+ const publicTarget = `${options.scope}/plugin-config/${[
645
+ options.sourceId,
646
+ options.pluginName,
647
+ CONFIG_FILE,
648
+ ].filter(Boolean).join('/')}`;
649
+ const { root, candidate } = assertConfigPathBoundary(allowedRoot, filePath);
650
+ let current = root;
651
+ try {
652
+ for (const segment of path.relative(root, path.dirname(candidate)).split(path.sep).filter(Boolean)) {
653
+ const stats = await lstatIfExists(current);
654
+ if (!stats) {
655
+ return {
656
+ pluginName: options.pluginName,
657
+ scope: options.scope,
658
+ storeKind: options.scope,
659
+ path: filePath,
660
+ existed: false,
661
+ deleted: false,
662
+ resetDisposition: 'absent',
663
+ };
664
+ }
665
+ if (stats.isSymbolicLink() || !stats.isDirectory()) {
666
+ throw new PluginConfigResetError('configResetTargetUnsupported', `${publicTarget} has an unsafe ancestor. Repair it manually, then retry config reset.`);
667
+ }
668
+ current = path.join(current, segment);
669
+ }
670
+ const parentStats = await lstatIfExists(current);
671
+ if (!parentStats) {
672
+ return {
673
+ pluginName: options.pluginName,
674
+ scope: options.scope,
675
+ storeKind: options.scope,
676
+ path: filePath,
677
+ existed: false,
678
+ deleted: false,
679
+ resetDisposition: 'absent',
680
+ };
681
+ }
682
+ if (parentStats.isSymbolicLink() || !parentStats.isDirectory()) {
683
+ throw new PluginConfigResetError('configResetTargetUnsupported', `${publicTarget} has an unsafe ancestor. Repair it manually, then retry config reset.`);
684
+ }
685
+ await options.reviewPreflight?.();
686
+ const target = await lstatIfExists(filePath);
687
+ if (!target) {
688
+ return {
689
+ pluginName: options.pluginName,
690
+ scope: options.scope,
691
+ storeKind: options.scope,
692
+ path: filePath,
693
+ existed: false,
694
+ deleted: false,
695
+ resetDisposition: 'absent',
696
+ };
697
+ }
698
+ if (target.isFile() || target.isSymbolicLink()) {
699
+ await assertNoConfigSymlink(allowedRoot, path.dirname(filePath));
700
+ await nodeFs.unlink(filePath);
701
+ return {
702
+ pluginName: options.pluginName,
703
+ scope: options.scope,
704
+ storeKind: options.scope,
705
+ path: filePath,
706
+ existed: true,
707
+ deleted: true,
708
+ resetDisposition: target.isSymbolicLink() ? 'unlinkedSymlink' : 'deletedFile',
709
+ };
710
+ }
711
+ if (target.isDirectory()) {
712
+ const timestamp = new Date().toISOString().replace(/[-:]/gu, '').replace(/\..*$/u, '');
713
+ const quarantineBasename = `config.broken-${timestamp}-${crypto.randomUUID()}.json`;
714
+ await assertNoConfigSymlink(allowedRoot, path.dirname(filePath));
715
+ await nodeFs.rename(filePath, path.join(path.dirname(filePath), quarantineBasename));
716
+ return {
717
+ pluginName: options.pluginName,
718
+ scope: options.scope,
719
+ storeKind: options.scope,
720
+ path: filePath,
721
+ existed: true,
722
+ deleted: true,
723
+ resetDisposition: 'quarantinedDirectory',
724
+ quarantineBasename,
725
+ };
726
+ }
727
+ throw new PluginConfigResetError('configResetTargetUnsupported', `${publicTarget} is a special filesystem node. Move it manually, then retry config reset.`);
728
+ }
729
+ catch (error) {
730
+ if (error instanceof PluginConfigResetError)
731
+ throw error;
732
+ const osCode = error.code;
733
+ throw new PluginConfigResetError('configResetFailed', `Cannot reset ${publicTarget}${osCode ? ` (${osCode})` : ''}. Fix filesystem access, then retry config reset.`, osCode);
734
+ }
735
+ }
736
+ export async function inspectPluginConfigResetTarget(options) {
737
+ const filePath = resolvePluginConfigStorePath({
738
+ ...options,
739
+ ...(options.scope === 'project' ? { access: 'write' } : {}),
740
+ });
741
+ const allowedRoot = resolvePluginConfigAllowedRoot(options);
742
+ const publicTarget = `${options.scope}/plugin-config/${[
743
+ options.sourceId,
744
+ options.pluginName,
745
+ CONFIG_FILE,
746
+ ].filter(Boolean).join('/')}`;
747
+ const { root, candidate } = assertConfigPathBoundary(allowedRoot, filePath);
748
+ let current = root;
749
+ for (const segment of path.relative(root, path.dirname(candidate)).split(path.sep).filter(Boolean)) {
750
+ const stats = await lstatIfExists(current);
751
+ if (!stats)
752
+ return resetTargetInspection('absent');
753
+ if (stats.isSymbolicLink() || !stats.isDirectory()) {
754
+ throw new PluginConfigResetError('configResetTargetUnsupported', `${publicTarget} has an unsafe ancestor. Repair it manually, then retry config reset.`);
755
+ }
756
+ current = path.join(current, segment);
757
+ }
758
+ const parentStats = await lstatIfExists(current);
759
+ if (!parentStats)
760
+ return resetTargetInspection('absent');
761
+ if (parentStats.isSymbolicLink() || !parentStats.isDirectory()) {
762
+ throw new PluginConfigResetError('configResetTargetUnsupported', `${publicTarget} has an unsafe ancestor. Repair it manually, then retry config reset.`);
763
+ }
764
+ const target = await lstatIfExists(filePath);
765
+ if (!target)
766
+ return resetTargetInspection('absent');
767
+ const kind = target.isSymbolicLink() ? 'symbolicLink'
768
+ : target.isFile() ? 'regularFile'
769
+ : target.isDirectory() ? 'directory'
770
+ : 'special';
771
+ return resetTargetInspection(kind, target);
772
+ }
773
+ function resetTargetInspection(kind, stats) {
774
+ const fingerprint = `sha256:${crypto.createHash('sha256').update(JSON.stringify({
775
+ kind,
776
+ ...(stats ? {
777
+ dev: stats.dev,
778
+ ino: stats.ino,
779
+ mode: stats.mode,
780
+ size: stats.size,
781
+ mtimeMs: stats.mtimeMs,
782
+ ctimeMs: stats.ctimeMs,
783
+ } : {}),
784
+ })).digest('hex')}`;
785
+ return {
786
+ exists: kind !== 'absent',
787
+ kind,
788
+ resettable: kind !== 'special',
789
+ fingerprint,
790
+ };
791
+ }
792
+ export async function resetPluginConfigStores(options) {
793
+ await assertNoLegacyConfigCoexistence(options);
794
+ const targets = resolvePluginConfigStoreTargets({
795
+ ...options,
796
+ ...(options.scope === 'project' ? { access: 'write' } : {}),
797
+ });
798
+ const allowedRoot = resolvePluginConfigAllowedRoot(options);
799
+ const receipts = [];
800
+ const existing = [];
801
+ for (const target of targets) {
802
+ await prepareConfigFileParent(target.path, allowedRoot);
803
+ await assertNoConfigSymlink(allowedRoot, target.path);
804
+ const stats = await lstatIfExists(target.path);
805
+ existing.push(stats !== null);
806
+ if (!stats)
807
+ continue;
808
+ if (!stats.isFile() || stats.isSymbolicLink()) {
809
+ throw new Error(`Config target must be a regular file: ${target.path}`);
810
+ }
811
+ }
812
+ for (const [index, target] of targets.entries()) {
813
+ const deleted = await atomicRemoveConfigFile({
814
+ filePath: target.path,
815
+ allowedRoot,
816
+ });
817
+ receipts.push({
818
+ pluginName: options.pluginName,
819
+ scope: options.scope,
820
+ storeKind: target.storeKind,
821
+ path: target.path,
822
+ existed: existing[index],
823
+ deleted,
824
+ resetDisposition: deleted ? 'deletedFile' : 'absent',
825
+ });
826
+ }
827
+ return receipts;
828
+ }
829
+ function coerceConfigValue(key, field, value) {
830
+ let coerced = value;
831
+ if (field.type === 'number' && typeof value === 'string' && value.trim() !== '') {
832
+ coerced = Number(value);
833
+ }
834
+ else if (field.type === 'boolean' && typeof value === 'string') {
835
+ const normalized = value.trim().toLowerCase();
836
+ if (['true', '1', 'yes', 'y'].includes(normalized))
837
+ coerced = true;
838
+ if (['false', '0', 'no', 'n'].includes(normalized))
839
+ coerced = false;
840
+ }
841
+ const issues = validateCorePluginConfigValue(field, coerced, key);
842
+ if (issues.length > 0) {
843
+ throw new Error(formatPluginConfigIssue(issues[0]));
844
+ }
845
+ return coerced;
846
+ }
847
+ export async function coercePluginConfigValue(options) {
848
+ return coerceConfigValue(options.key, options.field, options.value);
849
+ }
850
+ function redactValue(field, value) {
851
+ return redactPluginConfigValue(field, value);
852
+ }
853
+ function sourceForStoreLayer(scope) {
854
+ return scope === 'user' ? 'user' : 'project';
855
+ }
856
+ async function applyConfigLayer(options) {
857
+ for (const [key, field] of Object.entries(options.schema)) {
858
+ if (!Object.hasOwn(options.entries, key)) {
859
+ continue;
860
+ }
861
+ const value = options.entries[key];
862
+ const validationIssues = validateCorePluginConfigValue(field, value, key);
863
+ if (validationIssues.length === 0) {
864
+ const redactedValue = redactValue(field, value);
865
+ options.values[key] = value;
866
+ options.redactedValues[key] = redactedValue;
867
+ options.sources[key] = options.source;
868
+ options.fieldStates[key] = {
869
+ effective: { value, redactedValue, source: options.source },
870
+ };
871
+ }
872
+ else {
873
+ const fallback = options.fieldStates[key]?.effective;
874
+ delete options.values[key];
875
+ delete options.redactedValues[key];
876
+ options.sources[key] = options.source;
877
+ const issues = validationIssues.map((issue) => ({
878
+ type: 'invalid-config',
879
+ key,
880
+ source: options.source,
881
+ message: formatPluginConfigIssue(issue),
882
+ }));
883
+ options.fieldStates[key] = {
884
+ invalidOverride: { source: options.source, issues },
885
+ ...(fallback ? { fallback } : {}),
886
+ };
887
+ }
888
+ }
889
+ }
890
+ export async function resolveEffectivePluginConfig(options) {
891
+ const pluginName = assertSafePluginConfigSegment(options.pluginName ?? options.manifest.name);
892
+ const schema = options.manifest.config?.fields;
893
+ if (!schema) {
894
+ throw new Error(`Plugin "${pluginName}" does not declare an AW config schema.`);
895
+ }
896
+ const projectRoot = resolveAwProjectRoot(options.cwd);
897
+ const userStore = await readPluginConfigStore({
898
+ sourceId: options.sourceId,
899
+ sourceIdentity: options.sourceIdentity,
900
+ pluginName,
901
+ scope: 'user',
902
+ cwd: options.cwd,
903
+ env: options.env,
904
+ });
905
+ const projectStore = options.targetScope === 'user'
906
+ ? undefined
907
+ : await readPluginConfigStore({
908
+ sourceId: options.sourceId,
909
+ sourceIdentity: options.sourceIdentity,
910
+ pluginName,
911
+ scope: 'project',
912
+ cwd: options.cwd,
913
+ env: options.env,
914
+ });
915
+ assertReadyPluginConfigStore(userStore);
916
+ if (projectStore)
917
+ assertReadyPluginConfigStore(projectStore);
918
+ const values = {};
919
+ const redactedValues = {};
920
+ const sources = {};
921
+ const fieldStates = {};
922
+ const ignoredUnknownKeys = [];
923
+ const dangerousKeyIssues = [];
924
+ const schemaKeys = new Set(Object.keys(schema));
925
+ for (const key of schemaKeys) {
926
+ sources[key] = 'missing';
927
+ }
928
+ for (const [key, field] of Object.entries(schema)) {
929
+ if (field.default === undefined) {
930
+ continue;
931
+ }
932
+ await applyConfigLayer({
933
+ values,
934
+ redactedValues,
935
+ sources,
936
+ fieldStates,
937
+ schema: { [key]: field },
938
+ entries: { [key]: field.default },
939
+ source: 'default',
940
+ });
941
+ }
942
+ const layers = options.targetScope === 'user'
943
+ ? [['user', userStore]]
944
+ : [
945
+ ['user', userStore],
946
+ ['project', projectStore],
947
+ ];
948
+ for (const [scope, store] of layers) {
949
+ const plainEntries = getStoreEntries(store.values);
950
+ const source = sourceForStoreLayer(scope);
951
+ const dangerousIssues = new Map(validatePluginConfigDocument({ fields: schema }, plainEntries).filter((issue) => issue.code === 'dangerousKey')
952
+ .map((issue) => [issue.path.field, issue]));
953
+ for (const key of Object.keys(plainEntries)) {
954
+ if (!schemaKeys.has(key)) {
955
+ const dangerousIssue = dangerousIssues.get(key);
956
+ if (dangerousIssue) {
957
+ dangerousKeyIssues.push({
958
+ type: 'invalid-config',
959
+ code: dangerousIssue.code,
960
+ key,
961
+ source,
962
+ message: formatPluginConfigIssue(dangerousIssue),
963
+ });
964
+ }
965
+ else {
966
+ ignoredUnknownKeys.push({ scope, source, key });
967
+ }
968
+ }
969
+ }
970
+ await applyConfigLayer({
971
+ values,
972
+ redactedValues,
973
+ sources,
974
+ fieldStates,
975
+ schema,
976
+ entries: plainEntries,
977
+ source,
978
+ });
979
+ }
980
+ const issues = [
981
+ ...Object.values(fieldStates).flatMap((state) => state.invalidOverride?.issues ?? []),
982
+ ...dangerousKeyIssues,
983
+ ];
984
+ for (const [key, field] of Object.entries(schema)) {
985
+ if (field.required && values[key] === undefined && !fieldStates[key]?.invalidOverride) {
986
+ issues.push({
987
+ type: 'missing-required',
988
+ key,
989
+ source: sources[key],
990
+ message: `Config "${key}" is required.`,
991
+ });
992
+ }
993
+ }
994
+ return {
995
+ pluginName,
996
+ projectRoot,
997
+ values,
998
+ redactedValues,
999
+ sources,
1000
+ fieldStates,
1001
+ issues,
1002
+ ignoredUnknownKeys,
1003
+ schema,
1004
+ stores: {
1005
+ user: userStore,
1006
+ ...(projectStore ? { project: projectStore } : {}),
1007
+ },
1008
+ };
1009
+ }
1010
+ export async function coercePluginConfigUpdates(options) {
1011
+ const output = {};
1012
+ for (const [key, value] of Object.entries(options.updates)) {
1013
+ const field = options.schema[key];
1014
+ if (!field) {
1015
+ throw new Error(`Unknown config key "${key}".`);
1016
+ }
1017
+ const coerced = await coercePluginConfigValue({
1018
+ key,
1019
+ field,
1020
+ value,
1021
+ });
1022
+ output[key] = coerced;
1023
+ }
1024
+ return output;
1025
+ }
1026
+ export async function resolveProjectArtifactRenderConfig(options) {
1027
+ if (!options.manifest.config?.fields) {
1028
+ const pluginName = assertSafePluginConfigSegment(options.pluginName ?? options.manifest.name);
1029
+ const [userStore, projectStore] = await Promise.all([
1030
+ readPluginConfigStore({
1031
+ sourceId: options.sourceId,
1032
+ sourceIdentity: options.sourceIdentity,
1033
+ pluginName,
1034
+ scope: 'user',
1035
+ cwd: options.cwd,
1036
+ env: options.env,
1037
+ }),
1038
+ readPluginConfigStore({
1039
+ sourceId: options.sourceId,
1040
+ sourceIdentity: options.sourceIdentity,
1041
+ pluginName,
1042
+ scope: 'project',
1043
+ cwd: options.cwd,
1044
+ env: options.env,
1045
+ }),
1046
+ ]);
1047
+ assertReadyPluginConfigStore(userStore);
1048
+ assertReadyPluginConfigStore(projectStore);
1049
+ return {
1050
+ pluginName,
1051
+ projectRoot: resolveAwProjectRoot(options.cwd),
1052
+ values: {},
1053
+ sources: {},
1054
+ issues: [],
1055
+ ignoredUnknownKeys: [],
1056
+ schema: {},
1057
+ stores: { user: userStore, project: projectStore },
1058
+ };
1059
+ }
1060
+ const effective = await resolveEffectivePluginConfig({
1061
+ ...options,
1062
+ targetScope: 'project',
1063
+ });
1064
+ return {
1065
+ pluginName: effective.pluginName,
1066
+ projectRoot: effective.projectRoot,
1067
+ values: effective.values,
1068
+ sources: effective.sources,
1069
+ issues: effective.issues,
1070
+ ignoredUnknownKeys: effective.ignoredUnknownKeys,
1071
+ schema: effective.schema,
1072
+ stores: {
1073
+ user: effective.stores.user,
1074
+ project: effective.stores.project,
1075
+ },
1076
+ };
1077
+ }
1078
+ async function resolveRedactedLayerValue(options) {
1079
+ try {
1080
+ const coerced = await coercePluginConfigValue({
1081
+ key: options.key,
1082
+ field: options.field,
1083
+ value: options.value,
1084
+ });
1085
+ return {
1086
+ value: redactValue(options.field, coerced),
1087
+ };
1088
+ }
1089
+ catch (error) {
1090
+ return {
1091
+ issue: {
1092
+ type: 'invalid-config',
1093
+ key: options.key,
1094
+ source: options.source,
1095
+ message: error instanceof Error ? error.message : String(error),
1096
+ },
1097
+ };
1098
+ }
1099
+ }
1100
+ export async function resolveProjectScopedPluginConfigView(options) {
1101
+ const effective = await resolveEffectivePluginConfig(options);
1102
+ const entries = {};
1103
+ for (const [key, field] of Object.entries(effective.schema)) {
1104
+ const entry = {
1105
+ key,
1106
+ effectiveValue: effective.redactedValues[key],
1107
+ effectiveSource: effective.sources[key] ?? 'missing',
1108
+ };
1109
+ if (field.default !== undefined) {
1110
+ const resolvedDefault = await resolveRedactedLayerValue({
1111
+ key,
1112
+ field,
1113
+ source: 'default',
1114
+ value: field.default,
1115
+ });
1116
+ if (resolvedDefault.value !== undefined) {
1117
+ entry.defaultValue = resolvedDefault.value;
1118
+ }
1119
+ }
1120
+ const userPlainEntries = getStoreEntries(effective.stores.user.values);
1121
+ if (Object.hasOwn(userPlainEntries, key)) {
1122
+ const resolvedUser = await resolveRedactedLayerValue({
1123
+ key,
1124
+ field,
1125
+ source: 'user',
1126
+ value: userPlainEntries[key],
1127
+ });
1128
+ if (resolvedUser.value !== undefined) {
1129
+ entry.userValue = resolvedUser.value;
1130
+ }
1131
+ }
1132
+ const projectPlainEntries = getStoreEntries(effective.stores.project.values);
1133
+ if (Object.hasOwn(projectPlainEntries, key)) {
1134
+ const resolvedProject = await resolveRedactedLayerValue({
1135
+ key,
1136
+ field,
1137
+ source: 'project',
1138
+ value: projectPlainEntries[key],
1139
+ });
1140
+ if (resolvedProject.value !== undefined) {
1141
+ entry.projectValue = resolvedProject.value;
1142
+ }
1143
+ }
1144
+ entries[key] = entry;
1145
+ }
1146
+ return {
1147
+ pluginName: effective.pluginName,
1148
+ projectRoot: effective.projectRoot,
1149
+ schema: effective.schema,
1150
+ stores: {
1151
+ user: effective.stores.user,
1152
+ project: effective.stores.project,
1153
+ },
1154
+ redactedValues: effective.redactedValues,
1155
+ sources: effective.sources,
1156
+ entries,
1157
+ issues: effective.issues,
1158
+ ignoredUnknownKeys: effective.ignoredUnknownKeys,
1159
+ };
1160
+ }
1161
+ function toConfigSourceLayer(source) {
1162
+ if (source === 'default') {
1163
+ return 'default';
1164
+ }
1165
+ if (source === 'user') {
1166
+ return 'user';
1167
+ }
1168
+ if (source === 'project') {
1169
+ return 'project';
1170
+ }
1171
+ return null;
1172
+ }
1173
+ function configFieldProjection(options) {
1174
+ const source = options.source ?? 'missing';
1175
+ const issues = options.issues ?? [];
1176
+ const presetSensitive = options.field.sensitive === true
1177
+ || (options.field.type === 'object'
1178
+ && (Object.values(options.field.properties ?? {}).some((child) => child.sensitive === true)
1179
+ || (options.field.additionalProperties !== false
1180
+ && options.field.additionalProperties?.sensitive === true)));
1181
+ return {
1182
+ value: options.value === undefined ? null : redactValue(options.field, options.value),
1183
+ source,
1184
+ matchedPreset: options.value === undefined || presetSensitive
1185
+ ? null
1186
+ : matchPluginConfigPreset(options.field, options.value)?.id ?? null,
1187
+ valid: issues.length === 0,
1188
+ issues,
1189
+ };
1190
+ }
1191
+ async function buildManifestFieldEntries(options) {
1192
+ const schema = options.plugin.manifest.config?.fields;
1193
+ if (!schema) {
1194
+ return [];
1195
+ }
1196
+ const userStore = await readPluginConfigStore({
1197
+ sourceId: options.plugin.sourceId,
1198
+ sourceIdentity: options.plugin.sourceIdentity,
1199
+ pluginName: options.plugin.manifest.name,
1200
+ scope: 'user',
1201
+ cwd: options.cwd,
1202
+ env: options.env,
1203
+ schema,
1204
+ });
1205
+ const projectStore = options.scope === 'user'
1206
+ ? undefined
1207
+ : await readPluginConfigStore({
1208
+ sourceId: options.plugin.sourceId,
1209
+ sourceIdentity: options.plugin.sourceIdentity,
1210
+ pluginName: options.plugin.manifest.name,
1211
+ scope: 'project',
1212
+ cwd: options.cwd,
1213
+ env: options.env,
1214
+ schema,
1215
+ });
1216
+ assertReadyPluginConfigStore(userStore);
1217
+ if (projectStore)
1218
+ assertReadyPluginConfigStore(projectStore);
1219
+ const userPlainEntries = getStoreEntries(userStore.values);
1220
+ const projectPlainEntries = projectStore ? getStoreEntries(projectStore.values) : {};
1221
+ const effective = await resolveEffectivePluginConfig({
1222
+ sourceId: options.plugin.sourceId,
1223
+ sourceIdentity: options.plugin.sourceIdentity,
1224
+ pluginName: options.plugin.manifest.name,
1225
+ manifest: options.plugin.manifest,
1226
+ targetScope: options.scope === 'user' ? 'user' : 'project',
1227
+ cwd: options.cwd,
1228
+ env: options.env,
1229
+ });
1230
+ const userOnlyKeys = codexNativeRefreshKeys(options.plugin.manifest);
1231
+ const effectiveFieldState = (key) => {
1232
+ const source = effective.sources[key];
1233
+ const sourceLayer = toConfigSourceLayer(source);
1234
+ const field = schema[key];
1235
+ const fieldIssues = effective.issues.filter((entry) => entry.key === key);
1236
+ const fieldState = effective.fieldStates[key];
1237
+ if (source === undefined || source === 'missing') {
1238
+ return {
1239
+ valueState: fieldIssues.length > 0 ? 'invalid' : 'default',
1240
+ effectiveValue: null,
1241
+ sourceLayer: null,
1242
+ ...configFieldProjection({ field, source: 'missing', issues: fieldIssues }),
1243
+ };
1244
+ }
1245
+ const issue = fieldIssues.find((entry) => entry.source === source);
1246
+ return {
1247
+ valueState: issue ? 'invalid' : source === 'default' ? 'default' : 'configured',
1248
+ effectiveValue: issue ? null : effective.redactedValues[key] ?? null,
1249
+ sourceLayer,
1250
+ ...(fieldState?.invalidOverride ? {
1251
+ invalidOverride: {
1252
+ sourceLayer: fieldState.invalidOverride.source,
1253
+ issues: fieldState.invalidOverride.issues,
1254
+ },
1255
+ } : {}),
1256
+ ...(fieldState?.fallback ? {
1257
+ fallback: {
1258
+ effectiveValue: fieldState.fallback.redactedValue,
1259
+ sourceLayer: fieldState.fallback.source,
1260
+ },
1261
+ } : {}),
1262
+ ...configFieldProjection({
1263
+ field,
1264
+ value: issue ? undefined : effective.values[key],
1265
+ source,
1266
+ issues: fieldIssues,
1267
+ }),
1268
+ };
1269
+ };
1270
+ const fields = [];
1271
+ for (const [key, field] of Object.entries(schema)) {
1272
+ const base = {
1273
+ key,
1274
+ title: field.title ?? key,
1275
+ kind: 'manifest',
1276
+ reserved: false,
1277
+ writable: options.scope !== 'project' || !userOnlyKeys.has(key),
1278
+ valueState: 'default',
1279
+ effectiveValue: null,
1280
+ sourceLayer: null,
1281
+ ...configFieldProjection({ field }),
1282
+ };
1283
+ if (options.scope === 'user') {
1284
+ if (Object.hasOwn(userPlainEntries, key)) {
1285
+ try {
1286
+ const value = await coercePluginConfigValue({
1287
+ key,
1288
+ field,
1289
+ value: userPlainEntries[key],
1290
+ });
1291
+ fields.push({
1292
+ ...base,
1293
+ valueState: 'configured',
1294
+ effectiveValue: redactValue(field, value),
1295
+ sourceLayer: 'user',
1296
+ storedValue: redactValue(field, value),
1297
+ storedSourceLayer: 'user',
1298
+ ...configFieldProjection({ field, value, source: 'user' }),
1299
+ });
1300
+ }
1301
+ catch {
1302
+ fields.push({
1303
+ ...base,
1304
+ valueState: 'invalid',
1305
+ sourceLayer: 'user',
1306
+ storedSourceLayer: 'user',
1307
+ valid: false,
1308
+ issues: [{ type: 'invalid-config', key, source: 'user' }],
1309
+ });
1310
+ }
1311
+ continue;
1312
+ }
1313
+ if (field.default !== undefined) {
1314
+ try {
1315
+ const value = await coercePluginConfigValue({
1316
+ key,
1317
+ field,
1318
+ value: field.default,
1319
+ });
1320
+ fields.push({
1321
+ ...base,
1322
+ effectiveValue: redactValue(field, value),
1323
+ sourceLayer: 'default',
1324
+ ...configFieldProjection({ field, value, source: 'default' }),
1325
+ });
1326
+ }
1327
+ catch {
1328
+ fields.push({
1329
+ ...base,
1330
+ valueState: 'invalid',
1331
+ sourceLayer: 'default',
1332
+ source: 'default',
1333
+ valid: false,
1334
+ issues: [{ type: 'invalid-config', key, source: 'default' }],
1335
+ });
1336
+ }
1337
+ continue;
1338
+ }
1339
+ fields.push(base);
1340
+ continue;
1341
+ }
1342
+ let storedProjectValue;
1343
+ if (options.scope === 'project' && Object.hasOwn(projectPlainEntries, key)) {
1344
+ try {
1345
+ const value = await coercePluginConfigValue({
1346
+ key,
1347
+ field,
1348
+ value: projectPlainEntries[key],
1349
+ });
1350
+ storedProjectValue = {
1351
+ storedValue: redactValue(field, value),
1352
+ storedSourceLayer: 'project',
1353
+ };
1354
+ }
1355
+ catch {
1356
+ storedProjectValue = { storedSourceLayer: 'project' };
1357
+ }
1358
+ }
1359
+ fields.push({ ...base, ...effectiveFieldState(key), ...storedProjectValue });
1360
+ }
1361
+ return fields;
1362
+ }
1363
+ async function buildLegacyStoreFieldEntries(options) {
1364
+ const locations = options.scope === 'effective'
1365
+ ? [
1366
+ { scope: 'user', sourceLayer: 'user' },
1367
+ { scope: 'project', sourceLayer: 'project' },
1368
+ ]
1369
+ : [{
1370
+ scope: options.scope,
1371
+ sourceLayer: options.scope === 'user' ? 'user' : 'project',
1372
+ }];
1373
+ const fields = new Map();
1374
+ for (const location of locations) {
1375
+ const store = await readPluginConfigStore({
1376
+ sourceId: options.sourceId,
1377
+ sourceIdentity: options.sourceIdentity,
1378
+ pluginName: options.pluginName,
1379
+ scope: location.scope,
1380
+ cwd: options.cwd,
1381
+ env: options.env,
1382
+ });
1383
+ for (const key of Object.keys(store.values)) {
1384
+ fields.set(key, {
1385
+ key,
1386
+ title: key,
1387
+ kind: 'manifest',
1388
+ reserved: false,
1389
+ writable: false,
1390
+ valueState: 'configured',
1391
+ effectiveValue: '[redacted]',
1392
+ sourceLayer: location.sourceLayer,
1393
+ value: '[redacted]',
1394
+ source: location.sourceLayer,
1395
+ matchedPreset: null,
1396
+ valid: true,
1397
+ issues: [],
1398
+ });
1399
+ }
1400
+ }
1401
+ return [...fields.values()].sort((left, right) => left.key.localeCompare(right.key));
1402
+ }
1403
+ export async function resolvePluginConfigSchemaAccess(options) {
1404
+ const pluginName = assertSafePluginConfigSegment(options.pluginName);
1405
+ if (options.currentPluginError) {
1406
+ throw options.currentPluginError;
1407
+ }
1408
+ if (!options.currentPlugin) {
1409
+ return {
1410
+ pluginName,
1411
+ plugin: null,
1412
+ };
1413
+ }
1414
+ return {
1415
+ pluginName,
1416
+ plugin: options.currentPlugin,
1417
+ schema: createConfigSchemaEvidenceFromCurrentCatalog(options.currentPlugin, await calculateConfigSchemaManifestDigestForPlugin(options.currentPlugin)),
1418
+ };
1419
+ }
1420
+ export async function resolveConfigRead(options) {
1421
+ const pluginName = assertSafePluginConfigSegment(options.pluginName);
1422
+ const schemaAccess = await resolvePluginConfigSchemaAccess({
1423
+ pluginName,
1424
+ currentPlugin: options.plugin,
1425
+ currentPluginError: options.pluginError,
1426
+ scope: options.scope,
1427
+ cwd: options.cwd,
1428
+ env: options.env,
1429
+ });
1430
+ const manifestFields = schemaAccess.plugin
1431
+ ? await buildManifestFieldEntries({
1432
+ plugin: schemaAccess.plugin,
1433
+ scope: options.scope,
1434
+ cwd: options.cwd,
1435
+ env: options.env,
1436
+ })
1437
+ : await buildLegacyStoreFieldEntries({
1438
+ sourceId: options.sourceId,
1439
+ sourceIdentity: options.sourceIdentity,
1440
+ pluginName,
1441
+ scope: options.scope,
1442
+ cwd: options.cwd,
1443
+ env: options.env,
1444
+ });
1445
+ return {
1446
+ pluginName,
1447
+ scope: options.scope,
1448
+ ...(schemaAccess.schema ? { schema: schemaAccess.schema } : {}),
1449
+ fields: manifestFields,
1450
+ };
1451
+ }
1452
+ export function findConfigFieldEntry(fields, key) {
1453
+ return fields.find((field) => field.key === key) ?? null;
1454
+ }
1455
+ export function hasConfigReadContent(result) {
1456
+ return result.fields.length > 0;
1457
+ }
1458
+ export function summarizeEffectivePluginConfig(config) {
1459
+ return {
1460
+ values: config.redactedValues,
1461
+ sources: config.sources,
1462
+ fieldStates: Object.fromEntries(Object.entries(config.fieldStates).map(([key, state]) => [key, {
1463
+ ...(state.effective ? {
1464
+ effective: { value: state.effective.redactedValue, source: state.effective.source },
1465
+ } : {}),
1466
+ ...(state.invalidOverride ? { invalidOverride: state.invalidOverride } : {}),
1467
+ ...(state.fallback ? {
1468
+ fallback: { value: state.fallback.redactedValue, source: state.fallback.source },
1469
+ } : {}),
1470
+ }])),
1471
+ issues: config.issues,
1472
+ ignoredUnknownKeys: config.ignoredUnknownKeys,
1473
+ };
1474
+ }
1475
+ export function createPluginConfigSchemaIdentity(manifest) {
1476
+ const hash = crypto
1477
+ .createHash('sha256')
1478
+ .update(JSON.stringify({
1479
+ name: manifest.name,
1480
+ version: manifest.version,
1481
+ config: manifest.config ?? null,
1482
+ }))
1483
+ .digest('hex');
1484
+ return `${manifest.name}:${hash}`;
1485
+ }
1486
+ function cliConfigPromptEnumValues(field) {
1487
+ if (!field.choices)
1488
+ return undefined;
1489
+ const values = field.choices.presets
1490
+ .map((preset) => preset.value);
1491
+ if (field.type !== 'boolean') {
1492
+ return field.choices.allowCustom ? undefined : values;
1493
+ }
1494
+ if (field.choices.allowCustom) {
1495
+ for (const value of [true, false]) {
1496
+ if (!values.includes(value))
1497
+ values.push(value);
1498
+ }
1499
+ }
1500
+ return values;
1501
+ }
1502
+ export function toCliConfigPromptSchema(schema) {
1503
+ const promptSchema = {};
1504
+ for (const [key, field] of Object.entries(schema)) {
1505
+ if (field.type === 'object')
1506
+ continue;
1507
+ promptSchema[key] = {
1508
+ type: field.type,
1509
+ label: field.title ?? key,
1510
+ description: field.description,
1511
+ required: field.required,
1512
+ sensitive: field.sensitive,
1513
+ default: field.default,
1514
+ enum: cliConfigPromptEnumValues(field),
1515
+ min: field.type === 'number' ? field.validation?.minimum : undefined,
1516
+ max: field.type === 'number' ? field.validation?.maximum : undefined,
1517
+ };
1518
+ }
1519
+ return promptSchema;
1520
+ }
1521
+ export function createPluginConfigFormModel(options) {
1522
+ const schema = options.manifest.config?.fields;
1523
+ if (!schema) {
1524
+ throw new Error(`Plugin "${options.pluginName ?? options.manifest.name}" does not declare an AW config schema.`);
1525
+ }
1526
+ const requiredKeys = [...new Set(options.effective.issues
1527
+ .filter((issue) => issue.type === 'missing-required' || issue.type === 'invalid-config')
1528
+ .map((issue) => issue.key))];
1529
+ const fields = Object.entries(schema).flatMap(([key, field]) => {
1530
+ if (field.type === 'object')
1531
+ return [];
1532
+ return [{
1533
+ key,
1534
+ type: field.type,
1535
+ label: field.title ?? key,
1536
+ description: field.description,
1537
+ required: field.required === true,
1538
+ sensitive: field.sensitive === true,
1539
+ defaultValue: field.default,
1540
+ enumValues: cliConfigPromptEnumValues(field),
1541
+ min: field.type === 'number' ? field.validation?.minimum : undefined,
1542
+ max: field.type === 'number' ? field.validation?.maximum : undefined,
1543
+ configured: options.effective.values[key] !== undefined,
1544
+ currentValue: field.sensitive
1545
+ ? undefined
1546
+ : options.effective.values[key],
1547
+ }];
1548
+ });
1549
+ return {
1550
+ pluginName: options.pluginName ?? options.manifest.name,
1551
+ allowedScopes: options.allowedScopes ?? ['user', 'project'],
1552
+ initialScope: options.initialScope,
1553
+ requiredKeys,
1554
+ fields,
1555
+ };
1556
+ }
1557
+ export function toCliConfigPromptOptions(model) {
1558
+ const currentValues = {};
1559
+ const configuredKeys = [];
1560
+ for (const field of model.fields) {
1561
+ if (field.configured) {
1562
+ configuredKeys.push(field.key);
1563
+ }
1564
+ if (field.currentValue !== undefined) {
1565
+ currentValues[field.key] = field.currentValue;
1566
+ }
1567
+ }
1568
+ return {
1569
+ pluginName: model.pluginName,
1570
+ schema: Object.fromEntries(model.fields.map((field) => [
1571
+ field.key,
1572
+ {
1573
+ type: field.type,
1574
+ label: field.label,
1575
+ description: field.description,
1576
+ required: field.required,
1577
+ sensitive: field.sensitive,
1578
+ default: field.defaultValue,
1579
+ enum: field.enumValues,
1580
+ min: field.min,
1581
+ max: field.max,
1582
+ },
1583
+ ])),
1584
+ initialScope: model.initialScope,
1585
+ allowedScopes: [...model.allowedScopes],
1586
+ currentValues,
1587
+ configuredKeys,
1588
+ requiredKeys: [...model.requiredKeys],
1589
+ };
1590
+ }
1591
+ function formatRequiredConfigSetCommands(pluginName, issues, defaultScope) {
1592
+ const commands = new Set();
1593
+ for (const issue of issues) {
1594
+ const scope = issue.source === 'user' || issue.source === 'project'
1595
+ ? issue.source
1596
+ : defaultScope;
1597
+ commands.add(`aw config set ${pluginName} ${issue.key} --scope ${scope} --value-json '<json>'`);
1598
+ }
1599
+ return [...commands].map((command) => `\`${command}\``).join(' 或 ');
1600
+ }
1601
+ export async function preflightRequiredPluginConfigs(options) {
1602
+ const visited = new Set();
1603
+ const result = {
1604
+ checked: 0,
1605
+ prompted: 0,
1606
+ written: [],
1607
+ };
1608
+ for (const target of options.targets) {
1609
+ const pluginName = target.pluginName ?? target.manifest.name;
1610
+ const identity = target.schemaSourceIdentity ?? createPluginConfigSchemaIdentity(target.manifest);
1611
+ const dedupeKey = `${pluginName}\0${identity}`;
1612
+ if (visited.has(dedupeKey)) {
1613
+ continue;
1614
+ }
1615
+ visited.add(dedupeKey);
1616
+ result.checked += 1;
1617
+ if (!target.manifest.config?.fields) {
1618
+ continue;
1619
+ }
1620
+ let effective = await resolveEffectivePluginConfig({
1621
+ sourceId: target.sourceId,
1622
+ sourceIdentity: target.sourceIdentity,
1623
+ pluginName,
1624
+ manifest: target.manifest,
1625
+ cwd: options.cwd,
1626
+ env: options.context.env,
1627
+ });
1628
+ const blockingIssues = effective.issues.filter((issue) => issue.type === 'missing-required' || issue.type === 'invalid-config');
1629
+ if (blockingIssues.length === 0) {
1630
+ continue;
1631
+ }
1632
+ const details = blockingIssues.map((issue) => issue.key).join(', ');
1633
+ const setCommands = formatRequiredConfigSetCommands(pluginName, blockingIssues, options.defaultScope);
1634
+ throw new Error(`${pluginName} requires valid AW config before install/upgrade: ${details}. ` +
1635
+ `Run ${setCommands}, or open \`aw\` to complete setup.`);
1636
+ }
1637
+ return result;
1638
+ }
1639
+ export function renderPluginConfigValueForDisplay(value) {
1640
+ return formatValue(value);
1641
+ }
1642
+ //# sourceMappingURL=plugin-config.js.map