@ai-agent-forge/agent-forge 0.86.7 → 0.88.0

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 (717) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +10 -11
  3. package/dist/artifacts/artifact-store.d.ts.map +1 -1
  4. package/dist/artifacts/artifact-store.js +9 -5
  5. package/dist/artifacts/artifact-store.js.map +1 -1
  6. package/dist/bundle/.build-manifest.json +430 -413
  7. package/dist/bundle/chunks/{chunk-A7GUOX2H.js → chunk-6ZKDNZHA.js} +5 -1
  8. package/dist/bundle/chunks/chunk-HLJEVRWP.js +13 -0
  9. package/dist/bundle/chunks/chunk-YGHQME4V.js +1456 -0
  10. package/dist/bundle/chunks/image-resize-worker.js +12 -1
  11. package/dist/bundle/chunks/{virtual-modules-3IZ4MYSQ.js → virtual-modules-KZ5DRH7Q.js} +1 -1
  12. package/dist/bundle/cli.js +1 -1
  13. package/dist/bundle/client.js +1 -1
  14. package/dist/bundle/index.js +2 -2
  15. package/dist/bundle/rpc-entry.js +1 -1
  16. package/dist/cli/args.d.ts.map +1 -1
  17. package/dist/cli/args.js +231 -185
  18. package/dist/cli/args.js.map +1 -1
  19. package/dist/cli/auth-check.d.ts.map +1 -1
  20. package/dist/cli/auth-check.js +17 -4
  21. package/dist/cli/auth-check.js.map +1 -1
  22. package/dist/cli/auth-command.d.ts.map +1 -1
  23. package/dist/cli/auth-command.js +15 -9
  24. package/dist/cli/auth-command.js.map +1 -1
  25. package/dist/cli/credential-print.d.ts.map +1 -1
  26. package/dist/cli/credential-print.js +14 -7
  27. package/dist/cli/credential-print.js.map +1 -1
  28. package/dist/cli/experimental/command.d.ts.map +1 -1
  29. package/dist/cli/experimental/command.js +6 -5
  30. package/dist/cli/experimental/command.js.map +1 -1
  31. package/dist/cli/list-models.d.ts.map +1 -1
  32. package/dist/cli/list-models.js +9 -8
  33. package/dist/cli/list-models.js.map +1 -1
  34. package/dist/cli/plugin-manager-controller.d.ts.map +1 -1
  35. package/dist/cli/plugin-manager-controller.js +2 -1
  36. package/dist/cli/plugin-manager-controller.js.map +1 -1
  37. package/dist/cli/serve.d.ts.map +1 -1
  38. package/dist/cli/serve.js +8 -5
  39. package/dist/cli/serve.js.map +1 -1
  40. package/dist/client/remote-session.d.ts.map +1 -1
  41. package/dist/client/remote-session.js +16 -9
  42. package/dist/client/remote-session.js.map +1 -1
  43. package/dist/config.d.ts.map +1 -1
  44. package/dist/config.js +5 -3
  45. package/dist/config.js.map +1 -1
  46. package/dist/core/agent-capability-contract.d.ts +0 -7
  47. package/dist/core/agent-capability-contract.d.ts.map +1 -1
  48. package/dist/core/agent-capability-contract.js +72 -68
  49. package/dist/core/agent-capability-contract.js.map +1 -1
  50. package/dist/core/agent-session-bash.d.ts.map +1 -1
  51. package/dist/core/agent-session-bash.js +5 -4
  52. package/dist/core/agent-session-bash.js.map +1 -1
  53. package/dist/core/agent-session-compaction.d.ts +6 -10
  54. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  55. package/dist/core/agent-session-compaction.js +35 -133
  56. package/dist/core/agent-session-compaction.js.map +1 -1
  57. package/dist/core/agent-session-context.d.ts.map +1 -1
  58. package/dist/core/agent-session-context.js +18 -13
  59. package/dist/core/agent-session-context.js.map +1 -1
  60. package/dist/core/agent-session-internal.d.ts +0 -6
  61. package/dist/core/agent-session-internal.d.ts.map +1 -1
  62. package/dist/core/agent-session-internal.js.map +1 -1
  63. package/dist/core/agent-session-model.d.ts.map +1 -1
  64. package/dist/core/agent-session-model.js +2 -1
  65. package/dist/core/agent-session-model.js.map +1 -1
  66. package/dist/core/agent-session-retry.d.ts.map +1 -1
  67. package/dist/core/agent-session-retry.js +3 -3
  68. package/dist/core/agent-session-retry.js.map +1 -1
  69. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  70. package/dist/core/agent-session-runtime.js +7 -6
  71. package/dist/core/agent-session-runtime.js.map +1 -1
  72. package/dist/core/agent-session-services.d.ts +1 -1
  73. package/dist/core/agent-session-services.d.ts.map +1 -1
  74. package/dist/core/agent-session-services.js.map +1 -1
  75. package/dist/core/agent-session-tools.d.ts.map +1 -1
  76. package/dist/core/agent-session-tools.js +3 -2
  77. package/dist/core/agent-session-tools.js.map +1 -1
  78. package/dist/core/agent-session.d.ts +40 -22
  79. package/dist/core/agent-session.d.ts.map +1 -1
  80. package/dist/core/agent-session.js +147 -67
  81. package/dist/core/agent-session.js.map +1 -1
  82. package/dist/core/auth-guidance.d.ts.map +1 -1
  83. package/dist/core/auth-guidance.js +11 -5
  84. package/dist/core/auth-guidance.js.map +1 -1
  85. package/dist/core/auth-storage.d.ts.map +1 -1
  86. package/dist/core/auth-storage.js +9 -6
  87. package/dist/core/auth-storage.js.map +1 -1
  88. package/dist/core/compaction/compaction.d.ts.map +1 -1
  89. package/dist/core/compaction/compaction.js +2 -1
  90. package/dist/core/compaction/compaction.js.map +1 -1
  91. package/dist/core/embedded-sdk.d.ts.map +1 -1
  92. package/dist/core/embedded-sdk.js +10 -6
  93. package/dist/core/embedded-sdk.js.map +1 -1
  94. package/dist/core/export-html/index.d.ts.map +1 -1
  95. package/dist/core/export-html/index.js +9 -4
  96. package/dist/core/export-html/index.js.map +1 -1
  97. package/dist/core/extensions/agent-composition.d.ts.map +1 -1
  98. package/dist/core/extensions/agent-composition.js +26 -19
  99. package/dist/core/extensions/agent-composition.js.map +1 -1
  100. package/dist/core/extensions/bootstrap-agent-loop.d.ts.map +1 -1
  101. package/dist/core/extensions/bootstrap-agent-loop.js +3 -2
  102. package/dist/core/extensions/bootstrap-agent-loop.js.map +1 -1
  103. package/dist/core/extensions/bootstrap-compaction-policy.d.ts.map +1 -1
  104. package/dist/core/extensions/bootstrap-compaction-policy.js +0 -34
  105. package/dist/core/extensions/bootstrap-compaction-policy.js.map +1 -1
  106. package/dist/core/extensions/bootstrap-lifecycle.d.ts.map +1 -1
  107. package/dist/core/extensions/bootstrap-lifecycle.js +20 -17
  108. package/dist/core/extensions/bootstrap-lifecycle.js.map +1 -1
  109. package/dist/core/extensions/bootstrap-overflow-policy.d.ts.map +1 -1
  110. package/dist/core/extensions/bootstrap-overflow-policy.js +2 -1
  111. package/dist/core/extensions/bootstrap-overflow-policy.js.map +1 -1
  112. package/dist/core/extensions/bootstrap-retry-policy.d.ts.map +1 -1
  113. package/dist/core/extensions/bootstrap-retry-policy.js +4 -3
  114. package/dist/core/extensions/bootstrap-retry-policy.js.map +1 -1
  115. package/dist/core/extensions/bootstrap-skills.d.ts.map +1 -1
  116. package/dist/core/extensions/bootstrap-skills.js +7 -6
  117. package/dist/core/extensions/bootstrap-skills.js.map +1 -1
  118. package/dist/core/extensions/bootstrap-tools.d.ts.map +1 -1
  119. package/dist/core/extensions/bootstrap-tools.js +2 -1
  120. package/dist/core/extensions/bootstrap-tools.js.map +1 -1
  121. package/dist/core/extensions/capability-plugin.d.ts.map +1 -1
  122. package/dist/core/extensions/capability-plugin.js +37 -13
  123. package/dist/core/extensions/capability-plugin.js.map +1 -1
  124. package/dist/core/extensions/capability-runtime-events.d.ts.map +1 -1
  125. package/dist/core/extensions/capability-runtime-events.js +17 -13
  126. package/dist/core/extensions/capability-runtime-events.js.map +1 -1
  127. package/dist/core/extensions/capability-runtime-guards.d.ts.map +1 -1
  128. package/dist/core/extensions/capability-runtime-guards.js +16 -15
  129. package/dist/core/extensions/capability-runtime-guards.js.map +1 -1
  130. package/dist/core/extensions/capability-runtime-manifest.d.ts.map +1 -1
  131. package/dist/core/extensions/capability-runtime-manifest.js +52 -30
  132. package/dist/core/extensions/capability-runtime-manifest.js.map +1 -1
  133. package/dist/core/extensions/capability-runtime-validation.d.ts.map +1 -1
  134. package/dist/core/extensions/capability-runtime-validation.js +66 -65
  135. package/dist/core/extensions/capability-runtime-validation.js.map +1 -1
  136. package/dist/core/extensions/capability-runtime.d.ts.map +1 -1
  137. package/dist/core/extensions/capability-runtime.js +147 -127
  138. package/dist/core/extensions/capability-runtime.js.map +1 -1
  139. package/dist/core/extensions/compaction-memory-failure-mode.d.ts.map +1 -1
  140. package/dist/core/extensions/compaction-memory-failure-mode.js +3 -2
  141. package/dist/core/extensions/compaction-memory-failure-mode.js.map +1 -1
  142. package/dist/core/extensions/context-strategy-binding.d.ts.map +1 -1
  143. package/dist/core/extensions/context-strategy-binding.js +7 -4
  144. package/dist/core/extensions/context-strategy-binding.js.map +1 -1
  145. package/dist/core/extensions/lifecycle-catalog.d.ts.map +1 -1
  146. package/dist/core/extensions/lifecycle-catalog.js +290 -279
  147. package/dist/core/extensions/lifecycle-catalog.js.map +1 -1
  148. package/dist/core/extensions/lifecycle-delivery-queue.d.ts.map +1 -1
  149. package/dist/core/extensions/lifecycle-delivery-queue.js +3 -2
  150. package/dist/core/extensions/lifecycle-delivery-queue.js.map +1 -1
  151. package/dist/core/extensions/provider-failover.d.ts +7 -3
  152. package/dist/core/extensions/provider-failover.d.ts.map +1 -1
  153. package/dist/core/extensions/provider-failover.js +9 -8
  154. package/dist/core/extensions/provider-failover.js.map +1 -1
  155. package/dist/core/facade/coding-agent-facade.d.ts +6 -6
  156. package/dist/core/facade/coding-agent-facade.d.ts.map +1 -1
  157. package/dist/core/facade/coding-agent-facade.js +2 -3
  158. package/dist/core/facade/coding-agent-facade.js.map +1 -1
  159. package/dist/core/http-dispatcher.d.ts.map +1 -1
  160. package/dist/core/http-dispatcher.js +2 -1
  161. package/dist/core/http-dispatcher.js.map +1 -1
  162. package/dist/core/i18n/index.d.ts +14 -0
  163. package/dist/core/i18n/index.d.ts.map +1 -0
  164. package/dist/core/i18n/index.js +67 -0
  165. package/dist/core/i18n/index.js.map +1 -0
  166. package/dist/core/i18n/locales/en/example.d.ts +7 -0
  167. package/dist/core/i18n/locales/en/example.d.ts.map +1 -0
  168. package/dist/core/i18n/locales/en/example.js +7 -0
  169. package/dist/core/i18n/locales/en/example.js.map +1 -0
  170. package/dist/core/i18n/locales/zh-cn/cli.d.ts +8 -0
  171. package/dist/core/i18n/locales/zh-cn/cli.d.ts.map +1 -0
  172. package/dist/core/i18n/locales/zh-cn/cli.js +229 -0
  173. package/dist/core/i18n/locales/zh-cn/cli.js.map +1 -0
  174. package/dist/core/i18n/locales/zh-cn/common.d.ts +6 -0
  175. package/dist/core/i18n/locales/zh-cn/common.d.ts.map +1 -0
  176. package/dist/core/i18n/locales/zh-cn/common.js +6 -0
  177. package/dist/core/i18n/locales/zh-cn/common.js.map +1 -0
  178. package/dist/core/i18n/locales/zh-cn/core-contract-core.d.ts +13 -0
  179. package/dist/core/i18n/locales/zh-cn/core-contract-core.d.ts.map +1 -0
  180. package/dist/core/i18n/locales/zh-cn/core-contract-core.js +173 -0
  181. package/dist/core/i18n/locales/zh-cn/core-contract-core.js.map +1 -0
  182. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.d.ts +12 -0
  183. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.d.ts.map +1 -0
  184. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.js +582 -0
  185. package/dist/core/i18n/locales/zh-cn/core-contract-extensions.js.map +1 -0
  186. package/dist/core/i18n/locales/zh-cn/core-contract-final.d.ts +9 -0
  187. package/dist/core/i18n/locales/zh-cn/core-contract-final.d.ts.map +1 -0
  188. package/dist/core/i18n/locales/zh-cn/core-contract-final.js +55 -0
  189. package/dist/core/i18n/locales/zh-cn/core-contract-final.js.map +1 -0
  190. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.d.ts +11 -0
  191. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.d.ts.map +1 -0
  192. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.js +455 -0
  193. package/dist/core/i18n/locales/zh-cn/core-contract-plugins.js.map +1 -0
  194. package/dist/core/i18n/locales/zh-cn/core-errors.d.ts +9 -0
  195. package/dist/core/i18n/locales/zh-cn/core-errors.d.ts.map +1 -0
  196. package/dist/core/i18n/locales/zh-cn/core-errors.js +191 -0
  197. package/dist/core/i18n/locales/zh-cn/core-errors.js.map +1 -0
  198. package/dist/core/i18n/locales/zh-cn/example.d.ts +7 -0
  199. package/dist/core/i18n/locales/zh-cn/example.d.ts.map +1 -0
  200. package/dist/core/i18n/locales/zh-cn/example.js +10 -0
  201. package/dist/core/i18n/locales/zh-cn/example.js.map +1 -0
  202. package/dist/core/i18n/locales/zh-cn/index.d.ts +2 -0
  203. package/dist/core/i18n/locales/zh-cn/index.d.ts.map +1 -0
  204. package/dist/core/i18n/locales/zh-cn/index.js +41 -0
  205. package/dist/core/i18n/locales/zh-cn/index.js.map +1 -0
  206. package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts +10 -0
  207. package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts.map +1 -0
  208. package/dist/core/i18n/locales/zh-cn/interactive-mode.js +267 -0
  209. package/dist/core/i18n/locales/zh-cn/interactive-mode.js.map +1 -0
  210. package/dist/core/i18n/locales/zh-cn/keybindings.d.ts +11 -0
  211. package/dist/core/i18n/locales/zh-cn/keybindings.d.ts.map +1 -0
  212. package/dist/core/i18n/locales/zh-cn/keybindings.js +67 -0
  213. package/dist/core/i18n/locales/zh-cn/keybindings.js.map +1 -0
  214. package/dist/core/i18n/locales/zh-cn/slash-commands.d.ts +7 -0
  215. package/dist/core/i18n/locales/zh-cn/slash-commands.d.ts.map +1 -0
  216. package/dist/core/i18n/locales/zh-cn/slash-commands.js +39 -0
  217. package/dist/core/i18n/locales/zh-cn/slash-commands.js.map +1 -0
  218. package/dist/core/i18n/locales/zh-cn/tui-builtin.d.ts +10 -0
  219. package/dist/core/i18n/locales/zh-cn/tui-builtin.d.ts.map +1 -0
  220. package/dist/core/i18n/locales/zh-cn/tui-builtin.js +15 -0
  221. package/dist/core/i18n/locales/zh-cn/tui-builtin.js.map +1 -0
  222. package/dist/core/i18n/locales/zh-cn/tui-components-misc.d.ts +13 -0
  223. package/dist/core/i18n/locales/zh-cn/tui-components-misc.d.ts.map +1 -0
  224. package/dist/core/i18n/locales/zh-cn/tui-components-misc.js +102 -0
  225. package/dist/core/i18n/locales/zh-cn/tui-components-misc.js.map +1 -0
  226. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.d.ts +12 -0
  227. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.d.ts.map +1 -0
  228. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.js +286 -0
  229. package/dist/core/i18n/locales/zh-cn/tui-components-selectors.js.map +1 -0
  230. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.d.ts +9 -0
  231. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.d.ts.map +1 -0
  232. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.js +79 -0
  233. package/dist/core/i18n/locales/zh-cn/tui-interactive-misc.js.map +1 -0
  234. package/dist/core/i18n/locales/zh-cn/tui-self-owned.d.ts +8 -0
  235. package/dist/core/i18n/locales/zh-cn/tui-self-owned.d.ts.map +1 -0
  236. package/dist/core/i18n/locales/zh-cn/tui-self-owned.js +72 -0
  237. package/dist/core/i18n/locales/zh-cn/tui-self-owned.js.map +1 -0
  238. package/dist/core/keybindings.d.ts +15 -0
  239. package/dist/core/keybindings.d.ts.map +1 -1
  240. package/dist/core/keybindings.js +18 -0
  241. package/dist/core/keybindings.js.map +1 -1
  242. package/dist/core/logger.d.ts.map +1 -1
  243. package/dist/core/logger.js +5 -1
  244. package/dist/core/logger.js.map +1 -1
  245. package/dist/core/model-config.d.ts +0 -1
  246. package/dist/core/model-config.d.ts.map +1 -1
  247. package/dist/core/model-config.js +9 -1
  248. package/dist/core/model-config.js.map +1 -1
  249. package/dist/core/model-registry.d.ts.map +1 -1
  250. package/dist/core/model-registry.js +2 -1
  251. package/dist/core/model-registry.js.map +1 -1
  252. package/dist/core/model-runtime.d.ts.map +1 -1
  253. package/dist/core/model-runtime.js +11 -7
  254. package/dist/core/model-runtime.js.map +1 -1
  255. package/dist/core/package-install-host.d.ts.map +1 -1
  256. package/dist/core/package-install-host.js +4 -3
  257. package/dist/core/package-install-host.js.map +1 -1
  258. package/dist/core/package-manager.d.ts.map +1 -1
  259. package/dist/core/package-manager.js +15 -10
  260. package/dist/core/package-manager.js.map +1 -1
  261. package/dist/core/package-resource-collection.d.ts.map +1 -1
  262. package/dist/core/package-resource-collection.js +2 -1
  263. package/dist/core/package-resource-collection.js.map +1 -1
  264. package/dist/core/package-source.d.ts.map +1 -1
  265. package/dist/core/package-source.js +3 -2
  266. package/dist/core/package-source.js.map +1 -1
  267. package/dist/core/provider-composer.d.ts +2 -2
  268. package/dist/core/provider-composer.d.ts.map +1 -1
  269. package/dist/core/provider-composer.js +29 -21
  270. package/dist/core/provider-composer.js.map +1 -1
  271. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  272. package/dist/core/remote-catalog-provider.js +6 -2
  273. package/dist/core/remote-catalog-provider.js.map +1 -1
  274. package/dist/core/resolve-config-value.d.ts.map +1 -1
  275. package/dist/core/resolve-config-value.js +15 -5
  276. package/dist/core/resolve-config-value.js.map +1 -1
  277. package/dist/core/sdk.d.ts +27 -5
  278. package/dist/core/sdk.d.ts.map +1 -1
  279. package/dist/core/sdk.js +80 -55
  280. package/dist/core/sdk.js.map +1 -1
  281. package/dist/core/session-cwd.d.ts.map +1 -1
  282. package/dist/core/session-cwd.js +5 -2
  283. package/dist/core/session-cwd.js.map +1 -1
  284. package/dist/core/session-export.d.ts.map +1 -1
  285. package/dist/core/session-export.js +2 -1
  286. package/dist/core/session-export.js.map +1 -1
  287. package/dist/core/session-manager.d.ts.map +1 -1
  288. package/dist/core/session-manager.js +39 -29
  289. package/dist/core/session-manager.js.map +1 -1
  290. package/dist/core/session-storage.d.ts.map +1 -1
  291. package/dist/core/session-storage.js +3 -3
  292. package/dist/core/session-storage.js.map +1 -1
  293. package/dist/core/settings-manager.d.ts +20 -20
  294. package/dist/core/settings-manager.d.ts.map +1 -1
  295. package/dist/core/settings-manager.js +47 -59
  296. package/dist/core/settings-manager.js.map +1 -1
  297. package/dist/core/slash-commands.d.ts.map +1 -1
  298. package/dist/core/slash-commands.js +53 -32
  299. package/dist/core/slash-commands.js.map +1 -1
  300. package/dist/core/system-prompt.d.ts.map +1 -1
  301. package/dist/core/system-prompt.js +25 -2
  302. package/dist/core/system-prompt.js.map +1 -1
  303. package/dist/core/tools/bash.d.ts.map +1 -1
  304. package/dist/core/tools/bash.js +11 -7
  305. package/dist/core/tools/bash.js.map +1 -1
  306. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  307. package/dist/core/tools/edit-diff.js +9 -4
  308. package/dist/core/tools/edit-diff.js.map +1 -1
  309. package/dist/core/tools/edit.d.ts.map +1 -1
  310. package/dist/core/tools/edit.js +4 -3
  311. package/dist/core/tools/edit.js.map +1 -1
  312. package/dist/core/tools/index.d.ts.map +1 -1
  313. package/dist/core/tools/index.js +3 -2
  314. package/dist/core/tools/index.js.map +1 -1
  315. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  316. package/dist/core/tools/output-accumulator.js +2 -1
  317. package/dist/core/tools/output-accumulator.js.map +1 -1
  318. package/dist/core/tools/read.d.ts.map +1 -1
  319. package/dist/core/tools/read.js +6 -2
  320. package/dist/core/tools/read.js.map +1 -1
  321. package/dist/core/tools/write.d.ts.map +1 -1
  322. package/dist/core/tools/write.js +2 -1
  323. package/dist/core/tools/write.js.map +1 -1
  324. package/dist/durable/durable-service.d.ts.map +1 -1
  325. package/dist/durable/durable-service.js +25 -18
  326. package/dist/durable/durable-service.js.map +1 -1
  327. package/dist/host/checkpoint-commit.d.ts.map +1 -1
  328. package/dist/host/checkpoint-commit.js +2 -1
  329. package/dist/host/checkpoint-commit.js.map +1 -1
  330. package/dist/host/manual-compaction.d.ts.map +1 -1
  331. package/dist/host/manual-compaction.js +2 -1
  332. package/dist/host/manual-compaction.js.map +1 -1
  333. package/dist/host/output-projection.d.ts +0 -13
  334. package/dist/host/output-projection.d.ts.map +1 -1
  335. package/dist/host/output-projection.js +16 -2
  336. package/dist/host/output-projection.js.map +1 -1
  337. package/dist/host/summary-retry.d.ts.map +1 -1
  338. package/dist/host/summary-retry.js +3 -2
  339. package/dist/host/summary-retry.js.map +1 -1
  340. package/dist/main.d.ts.map +1 -1
  341. package/dist/main.js +97 -60
  342. package/dist/main.js.map +1 -1
  343. package/dist/modes/interactive/components/agents-view.d.ts.map +1 -1
  344. package/dist/modes/interactive/components/agents-view.js +18 -15
  345. package/dist/modes/interactive/components/agents-view.js.map +1 -1
  346. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  347. package/dist/modes/interactive/components/assistant-message.js +6 -5
  348. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  349. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  350. package/dist/modes/interactive/components/bash-execution.js +12 -7
  351. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  352. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  353. package/dist/modes/interactive/components/bordered-loader.js +2 -1
  354. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  355. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  356. package/dist/modes/interactive/components/branch-summary-message.js +6 -5
  357. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  358. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  359. package/dist/modes/interactive/components/compaction-summary-message.js +9 -5
  360. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  361. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  362. package/dist/modes/interactive/components/config-selector.js +18 -15
  363. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  364. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  365. package/dist/modes/interactive/components/custom-entry.js +2 -1
  366. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  367. package/dist/modes/interactive/components/dialog-editor.d.ts.map +1 -1
  368. package/dist/modes/interactive/components/dialog-editor.js +5 -4
  369. package/dist/modes/interactive/components/dialog-editor.js.map +1 -1
  370. package/dist/modes/interactive/components/dialog-input.d.ts.map +1 -1
  371. package/dist/modes/interactive/components/dialog-input.js +2 -1
  372. package/dist/modes/interactive/components/dialog-input.js.map +1 -1
  373. package/dist/modes/interactive/components/dialog-selector.d.ts.map +1 -1
  374. package/dist/modes/interactive/components/dialog-selector.js +4 -3
  375. package/dist/modes/interactive/components/dialog-selector.js.map +1 -1
  376. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  377. package/dist/modes/interactive/components/first-time-setup.js +11 -10
  378. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  379. package/dist/modes/interactive/components/footer.d.ts +0 -2
  380. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  381. package/dist/modes/interactive/components/footer.js +8 -10
  382. package/dist/modes/interactive/components/footer.js.map +1 -1
  383. package/dist/modes/interactive/components/index.d.ts +1 -0
  384. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  385. package/dist/modes/interactive/components/index.js +1 -0
  386. package/dist/modes/interactive/components/index.js.map +1 -1
  387. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  388. package/dist/modes/interactive/components/keybinding-hints.js +3 -2
  389. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  390. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  391. package/dist/modes/interactive/components/login-dialog.js +12 -11
  392. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  393. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -1
  394. package/dist/modes/interactive/components/mermaid.js +6 -2
  395. package/dist/modes/interactive/components/mermaid.js.map +1 -1
  396. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  397. package/dist/modes/interactive/components/model-selector.js +28 -16
  398. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  399. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  400. package/dist/modes/interactive/components/oauth-selector.js +10 -9
  401. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  402. package/dist/modes/interactive/components/plugin-manager-entry.d.ts.map +1 -1
  403. package/dist/modes/interactive/components/plugin-manager-entry.js +22 -13
  404. package/dist/modes/interactive/components/plugin-manager-entry.js.map +1 -1
  405. package/dist/modes/interactive/components/plugin-selection-entry.d.ts.map +1 -1
  406. package/dist/modes/interactive/components/plugin-selection-entry.js +27 -12
  407. package/dist/modes/interactive/components/plugin-selection-entry.js.map +1 -1
  408. package/dist/modes/interactive/components/queue-manager-selector.d.ts +60 -0
  409. package/dist/modes/interactive/components/queue-manager-selector.d.ts.map +1 -0
  410. package/dist/modes/interactive/components/queue-manager-selector.js +166 -0
  411. package/dist/modes/interactive/components/queue-manager-selector.js.map +1 -0
  412. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  413. package/dist/modes/interactive/components/scoped-models-selector.js +16 -14
  414. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  415. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  416. package/dist/modes/interactive/components/session-selector.js +43 -31
  417. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  418. package/dist/modes/interactive/components/settings-selector.d.ts +4 -6
  419. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  420. package/dist/modes/interactive/components/settings-selector.js +115 -120
  421. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  422. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -1
  423. package/dist/modes/interactive/components/settings-submenu.js +5 -4
  424. package/dist/modes/interactive/components/settings-submenu.js.map +1 -1
  425. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  426. package/dist/modes/interactive/components/show-images-selector.js +3 -2
  427. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  428. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  429. package/dist/modes/interactive/components/skill-invocation-message.js +4 -3
  430. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  431. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  432. package/dist/modes/interactive/components/status-indicator.js +14 -5
  433. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  434. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  435. package/dist/modes/interactive/components/theme-selector.js +2 -1
  436. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  437. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  438. package/dist/modes/interactive/components/thinking-selector.js +11 -4
  439. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  440. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  441. package/dist/modes/interactive/components/tool-execution.js +8 -3
  442. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  443. package/dist/modes/interactive/components/tree-list.d.ts.map +1 -1
  444. package/dist/modes/interactive/components/tree-list.js +7 -6
  445. package/dist/modes/interactive/components/tree-list.js.map +1 -1
  446. package/dist/modes/interactive/components/tree-render.d.ts.map +1 -1
  447. package/dist/modes/interactive/components/tree-render.js +28 -23
  448. package/dist/modes/interactive/components/tree-render.js.map +1 -1
  449. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  450. package/dist/modes/interactive/components/tree-selector.js +9 -7
  451. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  452. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  453. package/dist/modes/interactive/components/user-message-selector.js +5 -4
  454. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  455. package/dist/modes/interactive/components/workflow-view.d.ts.map +1 -1
  456. package/dist/modes/interactive/components/workflow-view.js +36 -21
  457. package/dist/modes/interactive/components/workflow-view.js.map +1 -1
  458. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  459. package/dist/modes/interactive/external-editor.js +2 -1
  460. package/dist/modes/interactive/external-editor.js.map +1 -1
  461. package/dist/modes/interactive/interactive-auth.d.ts.map +1 -1
  462. package/dist/modes/interactive/interactive-auth.js +71 -40
  463. package/dist/modes/interactive/interactive-auth.js.map +1 -1
  464. package/dist/modes/interactive/interactive-commands.d.ts +1 -0
  465. package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
  466. package/dist/modes/interactive/interactive-commands.js +3 -0
  467. package/dist/modes/interactive/interactive-commands.js.map +1 -1
  468. package/dist/modes/interactive/interactive-dialog-ui.d.ts.map +1 -1
  469. package/dist/modes/interactive/interactive-dialog-ui.js +5 -5
  470. package/dist/modes/interactive/interactive-dialog-ui.js.map +1 -1
  471. package/dist/modes/interactive/interactive-format.d.ts.map +1 -1
  472. package/dist/modes/interactive/interactive-format.js +34 -18
  473. package/dist/modes/interactive/interactive-format.js.map +1 -1
  474. package/dist/modes/interactive/interactive-helpers.d.ts.map +1 -1
  475. package/dist/modes/interactive/interactive-helpers.js +7 -2
  476. package/dist/modes/interactive/interactive-helpers.js.map +1 -1
  477. package/dist/modes/interactive/interactive-mode.d.ts +4 -2
  478. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  479. package/dist/modes/interactive/interactive-mode.js +350 -279
  480. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  481. package/dist/modes/interactive/interactive-selectors.d.ts +9 -0
  482. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  483. package/dist/modes/interactive/interactive-selectors.js +79 -25
  484. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  485. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  486. package/dist/modes/interactive/theme/theme-controller.js +5 -1
  487. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  488. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  489. package/dist/modes/interactive/theme/theme.js +26 -23
  490. package/dist/modes/interactive/theme/theme.js.map +1 -1
  491. package/dist/modes/json-event.d.ts.map +1 -1
  492. package/dist/modes/json-event.js +3 -2
  493. package/dist/modes/json-event.js.map +1 -1
  494. package/dist/modes/print-mode.d.ts.map +1 -1
  495. package/dist/modes/print-mode.js +3 -2
  496. package/dist/modes/print-mode.js.map +1 -1
  497. package/dist/modes/rpc/rpc-client.d.ts +0 -8
  498. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  499. package/dist/modes/rpc/rpc-client.js +3 -14
  500. package/dist/modes/rpc/rpc-client.js.map +1 -1
  501. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  502. package/dist/modes/rpc/rpc-mode.js +0 -9
  503. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  504. package/dist/modes/rpc/rpc-types.d.ts +0 -19
  505. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  506. package/dist/modes/rpc/rpc-types.js.map +1 -1
  507. package/dist/package-manager-cli.d.ts.map +1 -1
  508. package/dist/package-manager-cli.js +28 -13
  509. package/dist/package-manager-cli.js.map +1 -1
  510. package/dist/plugins/ecosystem/catalog.d.ts.map +1 -1
  511. package/dist/plugins/ecosystem/catalog.js +3 -2
  512. package/dist/plugins/ecosystem/catalog.js.map +1 -1
  513. package/dist/plugins/ecosystem/cli.d.ts.map +1 -1
  514. package/dist/plugins/ecosystem/cli.js +2 -1
  515. package/dist/plugins/ecosystem/cli.js.map +1 -1
  516. package/dist/plugins/ecosystem/default-plugin-market.d.ts.map +1 -1
  517. package/dist/plugins/ecosystem/default-plugin-market.js +3 -2
  518. package/dist/plugins/ecosystem/default-plugin-market.js.map +1 -1
  519. package/dist/plugins/ecosystem/distribution.d.ts.map +1 -1
  520. package/dist/plugins/ecosystem/distribution.js +2 -1
  521. package/dist/plugins/ecosystem/distribution.js.map +1 -1
  522. package/dist/plugins/ecosystem/installer.d.ts.map +1 -1
  523. package/dist/plugins/ecosystem/installer.js +3 -2
  524. package/dist/plugins/ecosystem/installer.js.map +1 -1
  525. package/dist/plugins/ecosystem/node-disk-registry-source.d.ts.map +1 -1
  526. package/dist/plugins/ecosystem/node-disk-registry-source.js +20 -17
  527. package/dist/plugins/ecosystem/node-disk-registry-source.js.map +1 -1
  528. package/dist/plugins/ecosystem/node-distribution-host.d.ts.map +1 -1
  529. package/dist/plugins/ecosystem/node-distribution-host.js +35 -34
  530. package/dist/plugins/ecosystem/node-distribution-host.js.map +1 -1
  531. package/dist/plugins/ecosystem/node-npm-registry-source.d.ts.map +1 -1
  532. package/dist/plugins/ecosystem/node-npm-registry-source.js +35 -34
  533. package/dist/plugins/ecosystem/node-npm-registry-source.js.map +1 -1
  534. package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -1
  535. package/dist/plugins/ecosystem/node-package-install-host.js +9 -8
  536. package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -1
  537. package/dist/plugins/ecosystem/node-package-install-persistence.d.ts.map +1 -1
  538. package/dist/plugins/ecosystem/node-package-install-persistence.js +4 -3
  539. package/dist/plugins/ecosystem/node-package-install-persistence.js.map +1 -1
  540. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts +9 -2
  541. package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -1
  542. package/dist/plugins/ecosystem/node-plugin-bootstrap.js +44 -5
  543. package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -1
  544. package/dist/plugins/ecosystem/node-plugin-trust-root.d.ts.map +1 -1
  545. package/dist/plugins/ecosystem/node-plugin-trust-root.js +10 -9
  546. package/dist/plugins/ecosystem/node-plugin-trust-root.js.map +1 -1
  547. package/dist/plugins/ecosystem/node-registry-source.d.ts.map +1 -1
  548. package/dist/plugins/ecosystem/node-registry-source.js +31 -30
  549. package/dist/plugins/ecosystem/node-registry-source.js.map +1 -1
  550. package/dist/plugins/ecosystem/node-release-package-source.d.ts.map +1 -1
  551. package/dist/plugins/ecosystem/node-release-package-source.js +30 -29
  552. package/dist/plugins/ecosystem/node-release-package-source.js.map +1 -1
  553. package/dist/plugins/ecosystem/node-tar-writer.d.ts.map +1 -1
  554. package/dist/plugins/ecosystem/node-tar-writer.js +11 -10
  555. package/dist/plugins/ecosystem/node-tar-writer.js.map +1 -1
  556. package/dist/plugins/ecosystem/node-url-package-source.d.ts.map +1 -1
  557. package/dist/plugins/ecosystem/node-url-package-source.js +21 -20
  558. package/dist/plugins/ecosystem/node-url-package-source.js.map +1 -1
  559. package/dist/plugins/ecosystem/plugin-acquisition.d.ts.map +1 -1
  560. package/dist/plugins/ecosystem/plugin-acquisition.js +3 -2
  561. package/dist/plugins/ecosystem/plugin-acquisition.js.map +1 -1
  562. package/dist/plugins/ecosystem/plugin-bootstrap.d.ts +9 -1
  563. package/dist/plugins/ecosystem/plugin-bootstrap.d.ts.map +1 -1
  564. package/dist/plugins/ecosystem/plugin-bootstrap.js +5 -0
  565. package/dist/plugins/ecosystem/plugin-bootstrap.js.map +1 -1
  566. package/dist/plugins/ecosystem/registry-cli-adapter.d.ts.map +1 -1
  567. package/dist/plugins/ecosystem/registry-cli-adapter.js +2 -1
  568. package/dist/plugins/ecosystem/registry-cli-adapter.js.map +1 -1
  569. package/dist/plugins/ecosystem/registry.d.ts.map +1 -1
  570. package/dist/plugins/ecosystem/registry.js +35 -32
  571. package/dist/plugins/ecosystem/registry.js.map +1 -1
  572. package/dist/plugins/ecosystem/resolver.d.ts.map +1 -1
  573. package/dist/plugins/ecosystem/resolver.js +3 -2
  574. package/dist/plugins/ecosystem/resolver.js.map +1 -1
  575. package/dist/plugins/ecosystem/selection-store.d.ts.map +1 -1
  576. package/dist/plugins/ecosystem/selection-store.js +11 -10
  577. package/dist/plugins/ecosystem/selection-store.js.map +1 -1
  578. package/dist/plugins/ecosystem/selection.d.ts.map +1 -1
  579. package/dist/plugins/ecosystem/selection.js +8 -7
  580. package/dist/plugins/ecosystem/selection.js.map +1 -1
  581. package/dist/plugins/ecosystem/state.d.ts.map +1 -1
  582. package/dist/plugins/ecosystem/state.js +24 -23
  583. package/dist/plugins/ecosystem/state.js.map +1 -1
  584. package/dist/plugins/ecosystem/updates.d.ts.map +1 -1
  585. package/dist/plugins/ecosystem/updates.js +5 -4
  586. package/dist/plugins/ecosystem/updates.js.map +1 -1
  587. package/dist/plugins/mcp-http.d.ts +0 -17
  588. package/dist/plugins/mcp-http.d.ts.map +1 -1
  589. package/dist/plugins/mcp-http.js +30 -7
  590. package/dist/plugins/mcp-http.js.map +1 -1
  591. package/dist/plugins/plugin-supervisor.d.ts.map +1 -1
  592. package/dist/plugins/plugin-supervisor.js +19 -1
  593. package/dist/plugins/plugin-supervisor.js.map +1 -1
  594. package/dist/profile/first-roles.d.ts.map +1 -1
  595. package/dist/profile/first-roles.js +10 -1
  596. package/dist/profile/first-roles.js.map +1 -1
  597. package/dist/profile/goal-plugin.d.ts.map +1 -1
  598. package/dist/profile/goal-plugin.js +24 -5
  599. package/dist/profile/goal-plugin.js.map +1 -1
  600. package/dist/profile/identity.d.ts.map +1 -1
  601. package/dist/profile/identity.js +7 -6
  602. package/dist/profile/identity.js.map +1 -1
  603. package/dist/profile/lock.d.ts.map +1 -1
  604. package/dist/profile/lock.js +15 -1
  605. package/dist/profile/lock.js.map +1 -1
  606. package/dist/profile/plan-plugin.d.ts.map +1 -1
  607. package/dist/profile/plan-plugin.js +26 -13
  608. package/dist/profile/plan-plugin.js.map +1 -1
  609. package/dist/profile/plugin-profiles.d.ts.map +1 -1
  610. package/dist/profile/plugin-profiles.js +2 -1
  611. package/dist/profile/plugin-profiles.js.map +1 -1
  612. package/dist/profile/policy-manifest.d.ts.map +1 -1
  613. package/dist/profile/policy-manifest.js +5 -4
  614. package/dist/profile/policy-manifest.js.map +1 -1
  615. package/dist/profile/profile-registry.d.ts +0 -12
  616. package/dist/profile/profile-registry.d.ts.map +1 -1
  617. package/dist/profile/profile-registry.js +14 -1
  618. package/dist/profile/profile-registry.js.map +1 -1
  619. package/dist/profile/role-catalog.d.ts +0 -1
  620. package/dist/profile/role-catalog.d.ts.map +1 -1
  621. package/dist/profile/role-catalog.js +17 -15
  622. package/dist/profile/role-catalog.js.map +1 -1
  623. package/dist/profile/role-orchestrator.d.ts.map +1 -1
  624. package/dist/profile/role-orchestrator.js +8 -7
  625. package/dist/profile/role-orchestrator.js.map +1 -1
  626. package/dist/profile/role-runner.d.ts.map +1 -1
  627. package/dist/profile/role-runner.js +3 -2
  628. package/dist/profile/role-runner.js.map +1 -1
  629. package/dist/profile/root-facade.d.ts.map +1 -1
  630. package/dist/profile/root-facade.js +2 -1
  631. package/dist/profile/root-facade.js.map +1 -1
  632. package/dist/profile/skill-discovery-wiring.d.ts.map +1 -1
  633. package/dist/profile/skill-discovery-wiring.js +7 -2
  634. package/dist/profile/skill-discovery-wiring.js.map +1 -1
  635. package/dist/profile/state-store.d.ts.map +1 -1
  636. package/dist/profile/state-store.js +13 -9
  637. package/dist/profile/state-store.js.map +1 -1
  638. package/dist/profile/todo-plugin.d.ts.map +1 -1
  639. package/dist/profile/todo-plugin.js +9 -4
  640. package/dist/profile/todo-plugin.js.map +1 -1
  641. package/dist/profiles/builtin-suites.d.ts +13 -6
  642. package/dist/profiles/builtin-suites.d.ts.map +1 -1
  643. package/dist/profiles/builtin-suites.js +15 -6
  644. package/dist/profiles/builtin-suites.js.map +1 -1
  645. package/dist/profiles/suite-loader.d.ts +25 -1
  646. package/dist/profiles/suite-loader.d.ts.map +1 -1
  647. package/dist/profiles/suite-loader.js +34 -9
  648. package/dist/profiles/suite-loader.js.map +1 -1
  649. package/dist/remote/server-entry.d.ts.map +1 -1
  650. package/dist/remote/server-entry.js +3 -2
  651. package/dist/remote/server-entry.js.map +1 -1
  652. package/dist/remote/session-source.d.ts.map +1 -1
  653. package/dist/remote/session-source.js +8 -4
  654. package/dist/remote/session-source.js.map +1 -1
  655. package/dist/share/session-share.d.ts.map +1 -1
  656. package/dist/share/session-share.js +14 -2
  657. package/dist/share/session-share.js.map +1 -1
  658. package/dist/testing/plugin-testkit.d.ts.map +1 -1
  659. package/dist/testing/plugin-testkit.js +4 -3
  660. package/dist/testing/plugin-testkit.js.map +1 -1
  661. package/dist/testing/runtime-delivery-queue.d.ts.map +1 -1
  662. package/dist/testing/runtime-delivery-queue.js +2 -1
  663. package/dist/testing/runtime-delivery-queue.js.map +1 -1
  664. package/dist/testing/session-plugin-testkit.d.ts.map +1 -1
  665. package/dist/testing/session-plugin-testkit.js +10 -7
  666. package/dist/testing/session-plugin-testkit.js.map +1 -1
  667. package/dist/utils/ansi.d.ts.map +1 -1
  668. package/dist/utils/ansi.js +2 -1
  669. package/dist/utils/ansi.js.map +1 -1
  670. package/dist/utils/clipboard.d.ts.map +1 -1
  671. package/dist/utils/clipboard.js +7 -6
  672. package/dist/utils/clipboard.js.map +1 -1
  673. package/dist/utils/image-resize-worker.d.ts.map +1 -1
  674. package/dist/utils/image-resize-worker.js +3 -2
  675. package/dist/utils/image-resize-worker.js.map +1 -1
  676. package/dist/utils/photon.d.ts +2 -2
  677. package/dist/utils/photon.d.ts.map +1 -1
  678. package/dist/utils/photon.js +2 -2
  679. package/dist/utils/photon.js.map +1 -1
  680. package/dist/utils/shell.d.ts.map +1 -1
  681. package/dist/utils/shell.js +9 -8
  682. package/dist/utils/shell.js.map +1 -1
  683. package/dist/utils/tools-manager.d.ts.map +1 -1
  684. package/dist/utils/tools-manager.js +23 -13
  685. package/dist/utils/tools-manager.js.map +1 -1
  686. package/dist/utils/version-check.d.ts.map +1 -1
  687. package/dist/utils/version-check.js +5 -1
  688. package/dist/utils/version-check.js.map +1 -1
  689. package/docs/compaction.md +1 -3
  690. package/docs/docs.json +4 -0
  691. package/docs/index.md +1 -0
  692. package/docs/keybindings.md +3 -0
  693. package/docs/mcp.md +103 -0
  694. package/docs/models.md +14 -22
  695. package/docs/providers.md +0 -1
  696. package/docs/quickstart.md +1 -1
  697. package/docs/rpc.md +1 -26
  698. package/docs/sdk.md +4 -6
  699. package/docs/settings.md +10 -12
  700. package/docs/skills.md +1 -9
  701. package/docs/terminal-setup.md +2 -3
  702. package/docs/usage.md +7 -6
  703. package/examples/extensions/with-deps/package-lock.json +2 -2
  704. package/examples/extensions/with-deps/package.json +1 -1
  705. package/examples/extensions/with-deps/plugin.json +1 -1
  706. package/examples/sdk/10-settings.ts +2 -4
  707. package/examples/sdk/12-full-control.ts +1 -2
  708. package/package.json +1 -3
  709. package/dist/bundle/chunks/chunk-EALJ5DDX.js +0 -1452
  710. package/dist/core/extensions/microcompact.d.ts +0 -80
  711. package/dist/core/extensions/microcompact.d.ts.map +0 -1
  712. package/dist/core/extensions/microcompact.js +0 -134
  713. package/dist/core/extensions/microcompact.js.map +0 -1
  714. package/dist/server/create-harness.d.ts +0 -26
  715. package/dist/server/create-harness.d.ts.map +0 -1
  716. package/dist/server/create-harness.js +0 -106
  717. package/dist/server/create-harness.js.map +0 -1
@@ -33,10 +33,17 @@ export interface NodePluginBootstrapOptionsV1 {
33
33
  readonly signal?: AbortSignal;
34
34
  /** Full dependency override for deterministic tests; the env switch still applies. */
35
35
  readonly deps?: PluginBootstrapDepsV1;
36
+ /** Overrides the first-run progress line (stderr) for tests and hosts. */
37
+ readonly onPendingInstalls?: (count: number) => void;
36
38
  }
37
39
  export type NodePluginBootstrapResultV1 = PluginBootstrapResultV1 & {
38
- /** Present when the env switch short-circuited the run before any local comparison. */
39
- readonly skipped?: "disabled";
40
+ /**
41
+ * Present when a posture switch short-circuited the run before any local
42
+ * comparison: the wiring-level env off switch, or the offline posture
43
+ * (设计 §4.4 offline 姿态门, 2026-10-03 — offline installs cannot succeed
44
+ * and would only fail slowly, so the run is skipped immediately).
45
+ */
46
+ readonly skipped?: "disabled" | "offline";
40
47
  };
41
48
  /**
42
49
  * Run the startup bootstrap for one agent directory. The function resolves
@@ -1 +1 @@
1
- {"version":3,"file":"node-plugin-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AASrF,OAAO,EAGN,KAAK,sBAAsB,EAC3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,uBAAuB,EAAwB,MAAM,uBAAuB,CAAC;AAGvH,6EAA6E;AAC7E,eAAO,MAAM,mCAAmC,iCAAiC,CAAC;AAKlF,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,6BAA6B,EAAE,CAAC;IAC7D,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAC;IACzC,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,qBAAqB,CAAC;CACtC;AAED,MAAM,MAAM,2BAA2B,GAAG,uBAAuB,GAAG;IACnE,uFAAuF;IACvF,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;CAC9B,CAAC;AAkCF;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,4BAA4B,GACnC,OAAO,CAAC,2BAA2B,CAAC,CA+ItC","sourcesContent":["/**\n * Node wiring for the startup plugin bootstrap (design\n * `插件独立分发与启动装配设计.md` §4.4, §0.7).\n *\n * `AGENT_FORGE_PLUGIN_BOOTSTRAP=off` skips everything, including the local\n * comparison. The comparison dependency reads the global and project\n * plugin-state.json stores only — read-only, and it never constructs an HTTP\n * client, so an assembled environment starts offline (G3). Network objects\n * (npm acquisition host, install host) are built lazily inside the install\n * action and closed when the bootstrap finishes. Installs go through the\n * npm acquisition path exclusively and persist the `npm:<host>` source label\n * in the global plugin state (same prepare/install/activate/upsert\n * transaction shape as the CLI install flow, implemented as a library\n * function without going through the CLI layer).\n */\n\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../../config.ts\";\nimport type { PluginBootstrapFactoryEntryV1 } from \"./bootstrap-factory-manifest.ts\";\nimport { BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1 } from \"./bootstrap-factory-manifest.ts\";\nimport {\n\tDEFAULT_PLUGIN_STATE_FILE_V1,\n\tdefaultPluginHostFactsV1,\n\tdefaultPluginInstallStoreDirectoryV1,\n\tprojectPluginScopeRefV1,\n} from \"./default-active-bindings.ts\";\nimport { createPluginInstallerV1, DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1, type PluginInstallerV1 } from \"./installer.ts\";\nimport {\n\tcreateNodePackageInstallHostV1,\n\ttype NodePackageInstallHostV1,\n\ttype NodeZipPackageLimitsV1,\n} from \"./node-package-install-host.ts\";\nimport { createNodePluginAcquisitionHostV1, type NodePluginAcquisitionHostV1 } from \"./node-plugin-acquisition-host.ts\";\nimport { type PluginBootstrapDepsV1, type PluginBootstrapResultV1, runPluginBootstrapV1 } from \"./plugin-bootstrap.ts\";\nimport { createPluginStateStoreV1, type PluginStateStoreV1 } from \"./state.ts\";\n\n/** Environment variable disabling the startup bootstrap entirely (`off`). */\nexport const AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1 = \"AGENT_FORGE_PLUGIN_BOOTSTRAP\";\n\n/** The only recognized disabling value; every other value (and absence) keeps bootstrap on. */\nconst PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1 = \"off\";\n\nexport interface NodePluginBootstrapOptionsV1 {\n\treadonly agentDir: string;\n\treadonly cwd: string;\n\t/** Defaults to the frozen factory manifest. */\n\treadonly manifest?: readonly PluginBootstrapFactoryEntryV1[];\n\t/** Bare http(s) npm registry origin; forwarded to the acquisition host. */\n\treadonly npmRegistry?: string;\n\t/** npm registry bearer token; forwarded to the acquisition host. */\n\treadonly npmToken?: string;\n\treadonly limits?: NodeZipPackageLimitsV1;\n\t/** Deterministic fetch replacement for tests. */\n\treadonly fetch?: typeof globalThis.fetch;\n\treadonly signal?: AbortSignal;\n\t/** Full dependency override for deterministic tests; the env switch still applies. */\n\treadonly deps?: PluginBootstrapDepsV1;\n}\n\nexport type NodePluginBootstrapResultV1 = PluginBootstrapResultV1 & {\n\t/** Present when the env switch short-circuited the run before any local comparison. */\n\treadonly skipped?: \"disabled\";\n};\n\n/** Install-scoped boundaries; created lazily by the first install action. */\ninterface InstallSessionV1 {\n\treadonly acquisition: NodePluginAcquisitionHostV1;\n\treadonly installHost: NodePackageInstallHostV1;\n\treadonly installer: PluginInstallerV1;\n\treadonly stateStore: PluginStateStoreV1;\n}\n\nfunction readActiveVersions(\n\tstore: PluginStateStoreV1,\n\tscope: \"global\" | \"project\",\n\tscopeRef: string,\n\tsignal: AbortSignal | undefined,\n): Promise<ReadonlyMap<string, string>> {\n\treturn (async () => {\n\t\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\t\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\t\tthrow new Error(\n\t\t\t\t`plugin state read failed (${result.diagnostics[0]?.code ?? result.status}) for the ${scope} scope`,\n\t\t\t);\n\t\tconst versions = new Map<string, string>();\n\t\tfor (const record of result.snapshot.records) {\n\t\t\tif (record.state.scope !== scope || record.state.scopeRef !== scopeRef) continue;\n\t\t\t// Enabled is deliberately not filtered: a disabled plugin still owns\n\t\t\t// its active installation record, and bootstrap never touches it (G5).\n\t\t\tif (record.state.lifecycle !== \"active\") continue;\n\t\t\tversions.set(record.pluginId, record.pluginVersion);\n\t\t}\n\t\treturn versions;\n\t})();\n}\n\n/**\n * Run the startup bootstrap for one agent directory. The function resolves\n * with a structured result and never throws for per-plugin failures; only a\n * wiring-level construction error propagates to the caller (main.ts guards\n * startup with its own catch).\n */\nexport async function runNodePluginBootstrapV1(\n\toptions: NodePluginBootstrapOptionsV1,\n): Promise<NodePluginBootstrapResultV1> {\n\tif (process.env[AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1]?.trim() === PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([]),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"disabled\",\n\t\t});\n\t}\n\tif (options.deps !== undefined) {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps: options.deps,\n\t\t});\n\t}\n\tlet session: InstallSessionV1 | undefined;\n\tconst ensureSession = async (): Promise<InstallSessionV1> => {\n\t\tif (session !== undefined) return session;\n\t\tconst acquisition = createNodePluginAcquisitionHostV1({\n\t\t\t...(options.npmRegistry === undefined ? {} : { npmRegistry: options.npmRegistry }),\n\t\t\t...(options.npmToken === undefined ? {} : { npmToken: options.npmToken }),\n\t\t\t...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n\t\t\t...(options.limits === undefined ? {} : { limits: options.limits }),\n\t\t});\n\t\tconst installHost = await createNodePackageInstallHostV1({\n\t\t\topenArtifact: (input) => acquisition.openArtifact(input),\n\t\t\tinstallationDirectory: defaultPluginInstallStoreDirectoryV1(options.agentDir),\n\t\t\tpersistence: \"persistent\",\n\t\t\tsupportedScopes: [\"global\", \"project\"],\n\t\t\t...(options.limits === undefined ? {} : { ...options.limits }),\n\t\t});\n\t\tsession = {\n\t\t\tacquisition,\n\t\t\tinstallHost,\n\t\t\tinstaller: createPluginInstallerV1({ host: installHost }),\n\t\t\tstateStore: createPluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t};\n\t\treturn session;\n\t};\n\tconst deps: PluginBootstrapDepsV1 = {\n\t\tlistActive: async ({ signal }) => {\n\t\t\tconst globalVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"global\",\n\t\t\t\tDEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst projectVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.cwd, CONFIG_DIR_NAME, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"project\",\n\t\t\t\tprojectPluginScopeRefV1(options.cwd),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst [globalResult, projectResult] = await Promise.all([globalVersions, projectVersions]);\n\t\t\t// Project precedence mirrors active-binding resolution: a project\n\t\t\t// installation shadows the same plugin id from the global scope.\n\t\t\treturn new Map([...globalResult, ...projectResult]);\n\t\t},\n\t\tinstallFromNpm: async ({ npmName, range, signal }) => {\n\t\t\tconst active = await ensureSession();\n\t\t\t// npm refs only (§0.7): scoped or plain names with the shared minimal\n\t\t\t// range subset; no github/gitee/url/local ref can be expressed here.\n\t\t\tconst ref = range === undefined ? npmName : `${npmName}@${range}`;\n\t\t\tconst acquired = await active.acquisition.resolvePackage({ ref, ...(signal === undefined ? {} : { signal }) });\n\t\t\tlet released = false;\n\t\t\tconst release = async (): Promise<void> => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tawait acquired.release();\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst verified = active.installer.prepare({\n\t\t\t\t\tmanifest: acquired.manifest,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\thost: defaultPluginHostFactsV1(),\n\t\t\t\t});\n\t\t\t\tif (verified.status !== \"verified\")\n\t\t\t\t\tthrow new Error(`installer prepare rejected ${acquired.manifest.id}: ${verified.reason}`);\n\t\t\t\tconst installed = await active.installer.install({\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\tartifact: acquired.artifact,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (installed.status !== \"succeeded\")\n\t\t\t\t\tthrow new Error(`plugin install failed (${installed.error.code}) for ${acquired.manifest.id}`);\n\t\t\t\tconst activated = await active.installer.activate({\n\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (activated.status !== \"succeeded\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(`plugin activation failed (${activated.error.code}) for ${acquired.manifest.id}`);\n\t\t\t\t}\n\t\t\t\tconst persisted = await active.stateStore.upsert({\n\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (persisted.status !== \"ok\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(`plugin state persist failed (${persisted.status}) for ${acquired.manifest.id}`);\n\t\t\t\t}\n\t\t\t\treturn { pluginVersion: activated.installation.pluginVersion, source: acquired.source };\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t},\n\t\t...(options.signal === undefined ? {} : { signal: options.signal }),\n\t};\n\ttry {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps,\n\t\t});\n\t} finally {\n\t\tconst active = session;\n\t\tsession = undefined;\n\t\tif (active !== undefined) {\n\t\t\tawait active.installHost.close().catch(() => undefined);\n\t\t\tawait active.acquisition.close().catch(() => undefined);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"node-plugin-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AASrF,OAAO,EAGN,KAAK,sBAAsB,EAC3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,uBAAuB,EAAwB,MAAM,uBAAuB,CAAC;AAGvH,6EAA6E;AAC7E,eAAO,MAAM,mCAAmC,iCAAiC,CAAC;AAKlF,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,6BAA6B,EAAE,CAAC;IAC7D,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAC;IACzC,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,qBAAqB,CAAC;IACtC,0EAA0E;IAC1E,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,MAAM,2BAA2B,GAAG,uBAAuB,GAAG;IACnE;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAC1C,CAAC;AAqCF;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,4BAA4B,GACnC,OAAO,CAAC,2BAA2B,CAAC,CA4LtC","sourcesContent":["/**\n * Node wiring for the startup plugin bootstrap (design\n * `插件独立分发与启动装配设计.md` §4.4, §0.7).\n *\n * `AGENT_FORGE_PLUGIN_BOOTSTRAP=off` skips everything, including the local\n * comparison. The comparison dependency reads the global and project\n * plugin-state.json stores only — read-only, and it never constructs an HTTP\n * client, so an assembled environment starts offline (G3). Network objects\n * (npm acquisition host, install host) are built lazily inside the install\n * action and closed when the bootstrap finishes. Installs go through the\n * npm acquisition path exclusively and persist the `npm:<host>` source label\n * in the global plugin state (same prepare/install/activate/upsert\n * transaction shape as the CLI install flow, implemented as a library\n * function without going through the CLI layer).\n */\n\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../../config.ts\";\nimport { t } from \"../../core/i18n/index.ts\";\nimport { isOfflineModeEnabled } from \"../../core/package-process-runner.ts\";\nimport type { PluginBootstrapFactoryEntryV1 } from \"./bootstrap-factory-manifest.ts\";\nimport { BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1 } from \"./bootstrap-factory-manifest.ts\";\nimport {\n\tDEFAULT_PLUGIN_STATE_FILE_V1,\n\tdefaultPluginHostFactsV1,\n\tdefaultPluginInstallStoreDirectoryV1,\n\tprojectPluginScopeRefV1,\n} from \"./default-active-bindings.ts\";\nimport { createPluginInstallerV1, DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1, type PluginInstallerV1 } from \"./installer.ts\";\nimport {\n\tcreateNodePackageInstallHostV1,\n\ttype NodePackageInstallHostV1,\n\ttype NodeZipPackageLimitsV1,\n} from \"./node-package-install-host.ts\";\nimport { createNodePluginAcquisitionHostV1, type NodePluginAcquisitionHostV1 } from \"./node-plugin-acquisition-host.ts\";\nimport { type PluginBootstrapDepsV1, type PluginBootstrapResultV1, runPluginBootstrapV1 } from \"./plugin-bootstrap.ts\";\nimport { createPluginStateStoreV1, type PluginStateStoreV1 } from \"./state.ts\";\n\n/** Environment variable disabling the startup bootstrap entirely (`off`). */\nexport const AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1 = \"AGENT_FORGE_PLUGIN_BOOTSTRAP\";\n\n/** The only recognized disabling value; every other value (and absence) keeps bootstrap on. */\nconst PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1 = \"off\";\n\nexport interface NodePluginBootstrapOptionsV1 {\n\treadonly agentDir: string;\n\treadonly cwd: string;\n\t/** Defaults to the frozen factory manifest. */\n\treadonly manifest?: readonly PluginBootstrapFactoryEntryV1[];\n\t/** Bare http(s) npm registry origin; forwarded to the acquisition host. */\n\treadonly npmRegistry?: string;\n\t/** npm registry bearer token; forwarded to the acquisition host. */\n\treadonly npmToken?: string;\n\treadonly limits?: NodeZipPackageLimitsV1;\n\t/** Deterministic fetch replacement for tests. */\n\treadonly fetch?: typeof globalThis.fetch;\n\treadonly signal?: AbortSignal;\n\t/** Full dependency override for deterministic tests; the env switch still applies. */\n\treadonly deps?: PluginBootstrapDepsV1;\n\t/** Overrides the first-run progress line (stderr) for tests and hosts. */\n\treadonly onPendingInstalls?: (count: number) => void;\n}\n\nexport type NodePluginBootstrapResultV1 = PluginBootstrapResultV1 & {\n\t/**\n\t * Present when a posture switch short-circuited the run before any local\n\t * comparison: the wiring-level env off switch, or the offline posture\n\t * (设计 §4.4 offline 姿态门, 2026-10-03 — offline installs cannot succeed\n\t * and would only fail slowly, so the run is skipped immediately).\n\t */\n\treadonly skipped?: \"disabled\" | \"offline\";\n};\n\n/** Install-scoped boundaries; created lazily by the first install action. */\ninterface InstallSessionV1 {\n\treadonly acquisition: NodePluginAcquisitionHostV1;\n\treadonly installHost: NodePackageInstallHostV1;\n\treadonly installer: PluginInstallerV1;\n\treadonly stateStore: PluginStateStoreV1;\n}\n\nfunction readActiveVersions(\n\tstore: PluginStateStoreV1,\n\tscope: \"global\" | \"project\",\n\tscopeRef: string,\n\tsignal: AbortSignal | undefined,\n): Promise<ReadonlyMap<string, string>> {\n\treturn (async () => {\n\t\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\t\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\t\tthrow new Error(\n\t\t\t\tt(\"plugin state read failed ({detail}) for the {scope} scope\", {\n\t\t\t\t\tdetail: result.diagnostics[0]?.code ?? result.status,\n\t\t\t\t\tscope,\n\t\t\t\t}),\n\t\t\t);\n\t\tconst versions = new Map<string, string>();\n\t\tfor (const record of result.snapshot.records) {\n\t\t\tif (record.state.scope !== scope || record.state.scopeRef !== scopeRef) continue;\n\t\t\t// Enabled is deliberately not filtered: a disabled plugin still owns\n\t\t\t// its active installation record, and bootstrap never touches it (G5).\n\t\t\tif (record.state.lifecycle !== \"active\") continue;\n\t\t\tversions.set(record.pluginId, record.pluginVersion);\n\t\t}\n\t\treturn versions;\n\t})();\n}\n\n/**\n * Run the startup bootstrap for one agent directory. The function resolves\n * with a structured result and never throws for per-plugin failures; only a\n * wiring-level construction error propagates to the caller (main.ts guards\n * startup with its own catch).\n */\nexport async function runNodePluginBootstrapV1(\n\toptions: NodePluginBootstrapOptionsV1,\n): Promise<NodePluginBootstrapResultV1> {\n\tif (process.env[AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1]?.trim() === PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([]),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"disabled\",\n\t\t});\n\t}\n\tif (isOfflineModeEnabled()) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([\n\t\t\t\t{\n\t\t\t\t\tcode: \"bootstrap_skipped_offline\",\n\t\t\t\t\tmessage: t(\n\t\t\t\t\t\t\"Offline posture (AGENT_FORGE_OFFLINE) skips the startup plugin bootstrap; run `agent-forge plugin install <package>` explicitly to install plugins.\",\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t] as const),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"offline\",\n\t\t});\n\t}\n\tif (options.deps !== undefined) {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps: options.deps,\n\t\t\t...(options.onPendingInstalls === undefined ? {} : { onPendingInstalls: options.onPendingInstalls }),\n\t\t});\n\t}\n\tlet session: InstallSessionV1 | undefined;\n\tconst ensureSession = async (): Promise<InstallSessionV1> => {\n\t\tif (session !== undefined) return session;\n\t\tconst acquisition = createNodePluginAcquisitionHostV1({\n\t\t\t...(options.npmRegistry === undefined ? {} : { npmRegistry: options.npmRegistry }),\n\t\t\t...(options.npmToken === undefined ? {} : { npmToken: options.npmToken }),\n\t\t\t...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n\t\t\t...(options.limits === undefined ? {} : { limits: options.limits }),\n\t\t});\n\t\tconst installHost = await createNodePackageInstallHostV1({\n\t\t\topenArtifact: (input) => acquisition.openArtifact(input),\n\t\t\tinstallationDirectory: defaultPluginInstallStoreDirectoryV1(options.agentDir),\n\t\t\tpersistence: \"persistent\",\n\t\t\tsupportedScopes: [\"global\", \"project\"],\n\t\t\t...(options.limits === undefined ? {} : { ...options.limits }),\n\t\t});\n\t\tsession = {\n\t\t\tacquisition,\n\t\t\tinstallHost,\n\t\t\tinstaller: createPluginInstallerV1({ host: installHost }),\n\t\t\tstateStore: createPluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t};\n\t\treturn session;\n\t};\n\tconst deps: PluginBootstrapDepsV1 = {\n\t\tlistActive: async ({ signal }) => {\n\t\t\tconst globalVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"global\",\n\t\t\t\tDEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst projectVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.cwd, CONFIG_DIR_NAME, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"project\",\n\t\t\t\tprojectPluginScopeRefV1(options.cwd),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst [globalResult, projectResult] = await Promise.all([globalVersions, projectVersions]);\n\t\t\t// Project precedence mirrors active-binding resolution: a project\n\t\t\t// installation shadows the same plugin id from the global scope.\n\t\t\treturn new Map([...globalResult, ...projectResult]);\n\t\t},\n\t\tinstallFromNpm: async ({ npmName, range, signal }) => {\n\t\t\tconst active = await ensureSession();\n\t\t\t// npm refs only (§0.7): scoped or plain names with the shared minimal\n\t\t\t// range subset; no github/gitee/url/local ref can be expressed here.\n\t\t\tconst ref = range === undefined ? npmName : `${npmName}@${range}`;\n\t\t\tconst acquired = await active.acquisition.resolvePackage({ ref, ...(signal === undefined ? {} : { signal }) });\n\t\t\tlet released = false;\n\t\t\tconst release = async (): Promise<void> => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tawait acquired.release();\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst verified = active.installer.prepare({\n\t\t\t\t\tmanifest: acquired.manifest,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\thost: defaultPluginHostFactsV1(),\n\t\t\t\t});\n\t\t\t\tif (verified.status !== \"verified\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"installer prepare rejected {pluginId}: {reason}\", {\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t\treason: verified.reason,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst installed = await active.installer.install({\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\tartifact: acquired.artifact,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (installed.status !== \"succeeded\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin install failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\t\tcode: installed.error.code,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst activated = await active.installer.activate({\n\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (activated.status !== \"succeeded\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin activation failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\t\tcode: activated.error.code,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst persisted = await active.stateStore.upsert({\n\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (persisted.status !== \"ok\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin state persist failed ({status}) for {pluginId}\", {\n\t\t\t\t\t\t\tstatus: persisted.status,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn { pluginVersion: activated.installation.pluginVersion, source: acquired.source };\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t},\n\t\t...(options.signal === undefined ? {} : { signal: options.signal }),\n\t};\n\ttry {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps,\n\t\t\t// First-run progress line (设计 §4.4, 2026-10-03): a fresh install is\n\t\t\t// otherwise silent for the whole network install window.\n\t\t\tonPendingInstalls:\n\t\t\t\toptions.onPendingInstalls ??\n\t\t\t\t((count: number) => {\n\t\t\t\t\tconsole.error(t(\"First run: installing {count} factory plugins…\", { count }));\n\t\t\t\t}),\n\t\t});\n\t} finally {\n\t\tconst active = session;\n\t\tsession = undefined;\n\t\tif (active !== undefined) {\n\t\t\tawait active.installHost.close().catch(() => undefined);\n\t\t\tawait active.acquisition.close().catch(() => undefined);\n\t\t}\n\t}\n}\n"]}
@@ -15,6 +15,8 @@
15
15
  */
16
16
  import { join } from "node:path";
17
17
  import { CONFIG_DIR_NAME } from "../../config.js";
18
+ import { t } from "../../core/i18n/index.js";
19
+ import { isOfflineModeEnabled } from "../../core/package-process-runner.js";
18
20
  import { BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1 } from "./bootstrap-factory-manifest.js";
19
21
  import { DEFAULT_PLUGIN_STATE_FILE_V1, defaultPluginHostFactsV1, defaultPluginInstallStoreDirectoryV1, projectPluginScopeRefV1, } from "./default-active-bindings.js";
20
22
  import { createPluginInstallerV1, DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1 } from "./installer.js";
@@ -30,7 +32,10 @@ function readActiveVersions(store, scope, scopeRef, signal) {
30
32
  return (async () => {
31
33
  const result = await store.read({ ...(signal === undefined ? {} : { signal }) });
32
34
  if (result.status !== "ok" && result.status !== "missing")
33
- throw new Error(`plugin state read failed (${result.diagnostics[0]?.code ?? result.status}) for the ${scope} scope`);
35
+ throw new Error(t("plugin state read failed ({detail}) for the {scope} scope", {
36
+ detail: result.diagnostics[0]?.code ?? result.status,
37
+ scope,
38
+ }));
34
39
  const versions = new Map();
35
40
  for (const record of result.snapshot.records) {
36
41
  if (record.state.scope !== scope || record.state.scopeRef !== scopeRef)
@@ -61,10 +66,26 @@ export async function runNodePluginBootstrapV1(options) {
61
66
  skipped: "disabled",
62
67
  });
63
68
  }
69
+ if (isOfflineModeEnabled()) {
70
+ return Object.freeze({
71
+ version: 1,
72
+ status: "succeeded",
73
+ actions: Object.freeze([]),
74
+ diagnostics: Object.freeze([
75
+ {
76
+ code: "bootstrap_skipped_offline",
77
+ message: t("Offline posture (AGENT_FORGE_OFFLINE) skips the startup plugin bootstrap; run `agent-forge plugin install <package>` explicitly to install plugins."),
78
+ },
79
+ ]),
80
+ durationMs: 0,
81
+ skipped: "offline",
82
+ });
83
+ }
64
84
  if (options.deps !== undefined) {
65
85
  return await runPluginBootstrapV1({
66
86
  manifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,
67
87
  deps: options.deps,
88
+ ...(options.onPendingInstalls === undefined ? {} : { onPendingInstalls: options.onPendingInstalls }),
68
89
  });
69
90
  }
70
91
  let session;
@@ -121,7 +142,10 @@ export async function runNodePluginBootstrapV1(options) {
121
142
  host: defaultPluginHostFactsV1(),
122
143
  });
123
144
  if (verified.status !== "verified")
124
- throw new Error(`installer prepare rejected ${acquired.manifest.id}: ${verified.reason}`);
145
+ throw new Error(t("installer prepare rejected {pluginId}: {reason}", {
146
+ pluginId: acquired.manifest.id,
147
+ reason: verified.reason,
148
+ }));
125
149
  const installed = await active.installer.install({
126
150
  record: verified.record,
127
151
  scope: "global",
@@ -130,7 +154,10 @@ export async function runNodePluginBootstrapV1(options) {
130
154
  ...(signal === undefined ? {} : { signal }),
131
155
  });
132
156
  if (installed.status !== "succeeded")
133
- throw new Error(`plugin install failed (${installed.error.code}) for ${acquired.manifest.id}`);
157
+ throw new Error(t("plugin install failed ({code}) for {pluginId}", {
158
+ code: installed.error.code,
159
+ pluginId: acquired.manifest.id,
160
+ }));
134
161
  const activated = await active.installer.activate({
135
162
  installation: installed.installation,
136
163
  record: verified.record,
@@ -146,7 +173,10 @@ export async function runNodePluginBootstrapV1(options) {
146
173
  scopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,
147
174
  })
148
175
  .catch(() => undefined);
149
- throw new Error(`plugin activation failed (${activated.error.code}) for ${acquired.manifest.id}`);
176
+ throw new Error(t("plugin activation failed ({code}) for {pluginId}", {
177
+ code: activated.error.code,
178
+ pluginId: acquired.manifest.id,
179
+ }));
150
180
  }
151
181
  const persisted = await active.stateStore.upsert({
152
182
  installation: activated.installation,
@@ -163,7 +193,10 @@ export async function runNodePluginBootstrapV1(options) {
163
193
  scopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,
164
194
  })
165
195
  .catch(() => undefined);
166
- throw new Error(`plugin state persist failed (${persisted.status}) for ${acquired.manifest.id}`);
196
+ throw new Error(t("plugin state persist failed ({status}) for {pluginId}", {
197
+ status: persisted.status,
198
+ pluginId: acquired.manifest.id,
199
+ }));
167
200
  }
168
201
  return { pluginVersion: activated.installation.pluginVersion, source: acquired.source };
169
202
  }
@@ -177,6 +210,12 @@ export async function runNodePluginBootstrapV1(options) {
177
210
  return await runPluginBootstrapV1({
178
211
  manifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,
179
212
  deps,
213
+ // First-run progress line (设计 §4.4, 2026-10-03): a fresh install is
214
+ // otherwise silent for the whole network install window.
215
+ onPendingInstalls: options.onPendingInstalls ??
216
+ ((count) => {
217
+ console.error(t("First run: installing {count} factory plugins…", { count }));
218
+ }),
180
219
  });
181
220
  }
182
221
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"node-plugin-bootstrap.js","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-bootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Node wiring for the startup plugin bootstrap (design\n * `插件独立分发与启动装配设计.md` §4.4, §0.7).\n *\n * `AGENT_FORGE_PLUGIN_BOOTSTRAP=off` skips everything, including the local\n * comparison. The comparison dependency reads the global and project\n * plugin-state.json stores only — read-only, and it never constructs an HTTP\n * client, so an assembled environment starts offline (G3). Network objects\n * (npm acquisition host, install host) are built lazily inside the install\n * action and closed when the bootstrap finishes. Installs go through the\n * npm acquisition path exclusively and persist the `npm:<host>` source label\n * in the global plugin state (same prepare/install/activate/upsert\n * transaction shape as the CLI install flow, implemented as a library\n * function without going through the CLI layer).\n */\n\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../../config.ts\";\nimport type { PluginBootstrapFactoryEntryV1 } from \"./bootstrap-factory-manifest.ts\";\nimport { BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1 } from \"./bootstrap-factory-manifest.ts\";\nimport {\n\tDEFAULT_PLUGIN_STATE_FILE_V1,\n\tdefaultPluginHostFactsV1,\n\tdefaultPluginInstallStoreDirectoryV1,\n\tprojectPluginScopeRefV1,\n} from \"./default-active-bindings.ts\";\nimport { createPluginInstallerV1, DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1, type PluginInstallerV1 } from \"./installer.ts\";\nimport {\n\tcreateNodePackageInstallHostV1,\n\ttype NodePackageInstallHostV1,\n\ttype NodeZipPackageLimitsV1,\n} from \"./node-package-install-host.ts\";\nimport { createNodePluginAcquisitionHostV1, type NodePluginAcquisitionHostV1 } from \"./node-plugin-acquisition-host.ts\";\nimport { type PluginBootstrapDepsV1, type PluginBootstrapResultV1, runPluginBootstrapV1 } from \"./plugin-bootstrap.ts\";\nimport { createPluginStateStoreV1, type PluginStateStoreV1 } from \"./state.ts\";\n\n/** Environment variable disabling the startup bootstrap entirely (`off`). */\nexport const AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1 = \"AGENT_FORGE_PLUGIN_BOOTSTRAP\";\n\n/** The only recognized disabling value; every other value (and absence) keeps bootstrap on. */\nconst PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1 = \"off\";\n\nexport interface NodePluginBootstrapOptionsV1 {\n\treadonly agentDir: string;\n\treadonly cwd: string;\n\t/** Defaults to the frozen factory manifest. */\n\treadonly manifest?: readonly PluginBootstrapFactoryEntryV1[];\n\t/** Bare http(s) npm registry origin; forwarded to the acquisition host. */\n\treadonly npmRegistry?: string;\n\t/** npm registry bearer token; forwarded to the acquisition host. */\n\treadonly npmToken?: string;\n\treadonly limits?: NodeZipPackageLimitsV1;\n\t/** Deterministic fetch replacement for tests. */\n\treadonly fetch?: typeof globalThis.fetch;\n\treadonly signal?: AbortSignal;\n\t/** Full dependency override for deterministic tests; the env switch still applies. */\n\treadonly deps?: PluginBootstrapDepsV1;\n}\n\nexport type NodePluginBootstrapResultV1 = PluginBootstrapResultV1 & {\n\t/** Present when the env switch short-circuited the run before any local comparison. */\n\treadonly skipped?: \"disabled\";\n};\n\n/** Install-scoped boundaries; created lazily by the first install action. */\ninterface InstallSessionV1 {\n\treadonly acquisition: NodePluginAcquisitionHostV1;\n\treadonly installHost: NodePackageInstallHostV1;\n\treadonly installer: PluginInstallerV1;\n\treadonly stateStore: PluginStateStoreV1;\n}\n\nfunction readActiveVersions(\n\tstore: PluginStateStoreV1,\n\tscope: \"global\" | \"project\",\n\tscopeRef: string,\n\tsignal: AbortSignal | undefined,\n): Promise<ReadonlyMap<string, string>> {\n\treturn (async () => {\n\t\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\t\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\t\tthrow new Error(\n\t\t\t\t`plugin state read failed (${result.diagnostics[0]?.code ?? result.status}) for the ${scope} scope`,\n\t\t\t);\n\t\tconst versions = new Map<string, string>();\n\t\tfor (const record of result.snapshot.records) {\n\t\t\tif (record.state.scope !== scope || record.state.scopeRef !== scopeRef) continue;\n\t\t\t// Enabled is deliberately not filtered: a disabled plugin still owns\n\t\t\t// its active installation record, and bootstrap never touches it (G5).\n\t\t\tif (record.state.lifecycle !== \"active\") continue;\n\t\t\tversions.set(record.pluginId, record.pluginVersion);\n\t\t}\n\t\treturn versions;\n\t})();\n}\n\n/**\n * Run the startup bootstrap for one agent directory. The function resolves\n * with a structured result and never throws for per-plugin failures; only a\n * wiring-level construction error propagates to the caller (main.ts guards\n * startup with its own catch).\n */\nexport async function runNodePluginBootstrapV1(\n\toptions: NodePluginBootstrapOptionsV1,\n): Promise<NodePluginBootstrapResultV1> {\n\tif (process.env[AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1]?.trim() === PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([]),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"disabled\",\n\t\t});\n\t}\n\tif (options.deps !== undefined) {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps: options.deps,\n\t\t});\n\t}\n\tlet session: InstallSessionV1 | undefined;\n\tconst ensureSession = async (): Promise<InstallSessionV1> => {\n\t\tif (session !== undefined) return session;\n\t\tconst acquisition = createNodePluginAcquisitionHostV1({\n\t\t\t...(options.npmRegistry === undefined ? {} : { npmRegistry: options.npmRegistry }),\n\t\t\t...(options.npmToken === undefined ? {} : { npmToken: options.npmToken }),\n\t\t\t...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n\t\t\t...(options.limits === undefined ? {} : { limits: options.limits }),\n\t\t});\n\t\tconst installHost = await createNodePackageInstallHostV1({\n\t\t\topenArtifact: (input) => acquisition.openArtifact(input),\n\t\t\tinstallationDirectory: defaultPluginInstallStoreDirectoryV1(options.agentDir),\n\t\t\tpersistence: \"persistent\",\n\t\t\tsupportedScopes: [\"global\", \"project\"],\n\t\t\t...(options.limits === undefined ? {} : { ...options.limits }),\n\t\t});\n\t\tsession = {\n\t\t\tacquisition,\n\t\t\tinstallHost,\n\t\t\tinstaller: createPluginInstallerV1({ host: installHost }),\n\t\t\tstateStore: createPluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t};\n\t\treturn session;\n\t};\n\tconst deps: PluginBootstrapDepsV1 = {\n\t\tlistActive: async ({ signal }) => {\n\t\t\tconst globalVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"global\",\n\t\t\t\tDEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst projectVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.cwd, CONFIG_DIR_NAME, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"project\",\n\t\t\t\tprojectPluginScopeRefV1(options.cwd),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst [globalResult, projectResult] = await Promise.all([globalVersions, projectVersions]);\n\t\t\t// Project precedence mirrors active-binding resolution: a project\n\t\t\t// installation shadows the same plugin id from the global scope.\n\t\t\treturn new Map([...globalResult, ...projectResult]);\n\t\t},\n\t\tinstallFromNpm: async ({ npmName, range, signal }) => {\n\t\t\tconst active = await ensureSession();\n\t\t\t// npm refs only (§0.7): scoped or plain names with the shared minimal\n\t\t\t// range subset; no github/gitee/url/local ref can be expressed here.\n\t\t\tconst ref = range === undefined ? npmName : `${npmName}@${range}`;\n\t\t\tconst acquired = await active.acquisition.resolvePackage({ ref, ...(signal === undefined ? {} : { signal }) });\n\t\t\tlet released = false;\n\t\t\tconst release = async (): Promise<void> => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tawait acquired.release();\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst verified = active.installer.prepare({\n\t\t\t\t\tmanifest: acquired.manifest,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\thost: defaultPluginHostFactsV1(),\n\t\t\t\t});\n\t\t\t\tif (verified.status !== \"verified\")\n\t\t\t\t\tthrow new Error(`installer prepare rejected ${acquired.manifest.id}: ${verified.reason}`);\n\t\t\t\tconst installed = await active.installer.install({\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\tartifact: acquired.artifact,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (installed.status !== \"succeeded\")\n\t\t\t\t\tthrow new Error(`plugin install failed (${installed.error.code}) for ${acquired.manifest.id}`);\n\t\t\t\tconst activated = await active.installer.activate({\n\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (activated.status !== \"succeeded\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(`plugin activation failed (${activated.error.code}) for ${acquired.manifest.id}`);\n\t\t\t\t}\n\t\t\t\tconst persisted = await active.stateStore.upsert({\n\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (persisted.status !== \"ok\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(`plugin state persist failed (${persisted.status}) for ${acquired.manifest.id}`);\n\t\t\t\t}\n\t\t\t\treturn { pluginVersion: activated.installation.pluginVersion, source: acquired.source };\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t},\n\t\t...(options.signal === undefined ? {} : { signal: options.signal }),\n\t};\n\ttry {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps,\n\t\t});\n\t} finally {\n\t\tconst active = session;\n\t\tsession = undefined;\n\t\tif (active !== undefined) {\n\t\t\tawait active.installHost.close().catch(() => undefined);\n\t\t\tawait active.acquisition.close().catch(() => undefined);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"node-plugin-bootstrap.js","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-bootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Node wiring for the startup plugin bootstrap (design\n * `插件独立分发与启动装配设计.md` §4.4, §0.7).\n *\n * `AGENT_FORGE_PLUGIN_BOOTSTRAP=off` skips everything, including the local\n * comparison. The comparison dependency reads the global and project\n * plugin-state.json stores only — read-only, and it never constructs an HTTP\n * client, so an assembled environment starts offline (G3). Network objects\n * (npm acquisition host, install host) are built lazily inside the install\n * action and closed when the bootstrap finishes. Installs go through the\n * npm acquisition path exclusively and persist the `npm:<host>` source label\n * in the global plugin state (same prepare/install/activate/upsert\n * transaction shape as the CLI install flow, implemented as a library\n * function without going through the CLI layer).\n */\n\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME } from \"../../config.ts\";\nimport { t } from \"../../core/i18n/index.ts\";\nimport { isOfflineModeEnabled } from \"../../core/package-process-runner.ts\";\nimport type { PluginBootstrapFactoryEntryV1 } from \"./bootstrap-factory-manifest.ts\";\nimport { BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1 } from \"./bootstrap-factory-manifest.ts\";\nimport {\n\tDEFAULT_PLUGIN_STATE_FILE_V1,\n\tdefaultPluginHostFactsV1,\n\tdefaultPluginInstallStoreDirectoryV1,\n\tprojectPluginScopeRefV1,\n} from \"./default-active-bindings.ts\";\nimport { createPluginInstallerV1, DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1, type PluginInstallerV1 } from \"./installer.ts\";\nimport {\n\tcreateNodePackageInstallHostV1,\n\ttype NodePackageInstallHostV1,\n\ttype NodeZipPackageLimitsV1,\n} from \"./node-package-install-host.ts\";\nimport { createNodePluginAcquisitionHostV1, type NodePluginAcquisitionHostV1 } from \"./node-plugin-acquisition-host.ts\";\nimport { type PluginBootstrapDepsV1, type PluginBootstrapResultV1, runPluginBootstrapV1 } from \"./plugin-bootstrap.ts\";\nimport { createPluginStateStoreV1, type PluginStateStoreV1 } from \"./state.ts\";\n\n/** Environment variable disabling the startup bootstrap entirely (`off`). */\nexport const AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1 = \"AGENT_FORGE_PLUGIN_BOOTSTRAP\";\n\n/** The only recognized disabling value; every other value (and absence) keeps bootstrap on. */\nconst PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1 = \"off\";\n\nexport interface NodePluginBootstrapOptionsV1 {\n\treadonly agentDir: string;\n\treadonly cwd: string;\n\t/** Defaults to the frozen factory manifest. */\n\treadonly manifest?: readonly PluginBootstrapFactoryEntryV1[];\n\t/** Bare http(s) npm registry origin; forwarded to the acquisition host. */\n\treadonly npmRegistry?: string;\n\t/** npm registry bearer token; forwarded to the acquisition host. */\n\treadonly npmToken?: string;\n\treadonly limits?: NodeZipPackageLimitsV1;\n\t/** Deterministic fetch replacement for tests. */\n\treadonly fetch?: typeof globalThis.fetch;\n\treadonly signal?: AbortSignal;\n\t/** Full dependency override for deterministic tests; the env switch still applies. */\n\treadonly deps?: PluginBootstrapDepsV1;\n\t/** Overrides the first-run progress line (stderr) for tests and hosts. */\n\treadonly onPendingInstalls?: (count: number) => void;\n}\n\nexport type NodePluginBootstrapResultV1 = PluginBootstrapResultV1 & {\n\t/**\n\t * Present when a posture switch short-circuited the run before any local\n\t * comparison: the wiring-level env off switch, or the offline posture\n\t * (设计 §4.4 offline 姿态门, 2026-10-03 — offline installs cannot succeed\n\t * and would only fail slowly, so the run is skipped immediately).\n\t */\n\treadonly skipped?: \"disabled\" | \"offline\";\n};\n\n/** Install-scoped boundaries; created lazily by the first install action. */\ninterface InstallSessionV1 {\n\treadonly acquisition: NodePluginAcquisitionHostV1;\n\treadonly installHost: NodePackageInstallHostV1;\n\treadonly installer: PluginInstallerV1;\n\treadonly stateStore: PluginStateStoreV1;\n}\n\nfunction readActiveVersions(\n\tstore: PluginStateStoreV1,\n\tscope: \"global\" | \"project\",\n\tscopeRef: string,\n\tsignal: AbortSignal | undefined,\n): Promise<ReadonlyMap<string, string>> {\n\treturn (async () => {\n\t\tconst result = await store.read({ ...(signal === undefined ? {} : { signal }) });\n\t\tif (result.status !== \"ok\" && result.status !== \"missing\")\n\t\t\tthrow new Error(\n\t\t\t\tt(\"plugin state read failed ({detail}) for the {scope} scope\", {\n\t\t\t\t\tdetail: result.diagnostics[0]?.code ?? result.status,\n\t\t\t\t\tscope,\n\t\t\t\t}),\n\t\t\t);\n\t\tconst versions = new Map<string, string>();\n\t\tfor (const record of result.snapshot.records) {\n\t\t\tif (record.state.scope !== scope || record.state.scopeRef !== scopeRef) continue;\n\t\t\t// Enabled is deliberately not filtered: a disabled plugin still owns\n\t\t\t// its active installation record, and bootstrap never touches it (G5).\n\t\t\tif (record.state.lifecycle !== \"active\") continue;\n\t\t\tversions.set(record.pluginId, record.pluginVersion);\n\t\t}\n\t\treturn versions;\n\t})();\n}\n\n/**\n * Run the startup bootstrap for one agent directory. The function resolves\n * with a structured result and never throws for per-plugin failures; only a\n * wiring-level construction error propagates to the caller (main.ts guards\n * startup with its own catch).\n */\nexport async function runNodePluginBootstrapV1(\n\toptions: NodePluginBootstrapOptionsV1,\n): Promise<NodePluginBootstrapResultV1> {\n\tif (process.env[AGENT_FORGE_PLUGIN_BOOTSTRAP_ENV_V1]?.trim() === PLUGIN_BOOTSTRAP_DISABLED_VALUE_V1) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([]),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"disabled\",\n\t\t});\n\t}\n\tif (isOfflineModeEnabled()) {\n\t\treturn Object.freeze({\n\t\t\tversion: 1,\n\t\t\tstatus: \"succeeded\",\n\t\t\tactions: Object.freeze([]),\n\t\t\tdiagnostics: Object.freeze([\n\t\t\t\t{\n\t\t\t\t\tcode: \"bootstrap_skipped_offline\",\n\t\t\t\t\tmessage: t(\n\t\t\t\t\t\t\"Offline posture (AGENT_FORGE_OFFLINE) skips the startup plugin bootstrap; run `agent-forge plugin install <package>` explicitly to install plugins.\",\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t] as const),\n\t\t\tdurationMs: 0,\n\t\t\tskipped: \"offline\",\n\t\t});\n\t}\n\tif (options.deps !== undefined) {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps: options.deps,\n\t\t\t...(options.onPendingInstalls === undefined ? {} : { onPendingInstalls: options.onPendingInstalls }),\n\t\t});\n\t}\n\tlet session: InstallSessionV1 | undefined;\n\tconst ensureSession = async (): Promise<InstallSessionV1> => {\n\t\tif (session !== undefined) return session;\n\t\tconst acquisition = createNodePluginAcquisitionHostV1({\n\t\t\t...(options.npmRegistry === undefined ? {} : { npmRegistry: options.npmRegistry }),\n\t\t\t...(options.npmToken === undefined ? {} : { npmToken: options.npmToken }),\n\t\t\t...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n\t\t\t...(options.limits === undefined ? {} : { limits: options.limits }),\n\t\t});\n\t\tconst installHost = await createNodePackageInstallHostV1({\n\t\t\topenArtifact: (input) => acquisition.openArtifact(input),\n\t\t\tinstallationDirectory: defaultPluginInstallStoreDirectoryV1(options.agentDir),\n\t\t\tpersistence: \"persistent\",\n\t\t\tsupportedScopes: [\"global\", \"project\"],\n\t\t\t...(options.limits === undefined ? {} : { ...options.limits }),\n\t\t});\n\t\tsession = {\n\t\t\tacquisition,\n\t\t\tinstallHost,\n\t\t\tinstaller: createPluginInstallerV1({ host: installHost }),\n\t\t\tstateStore: createPluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t};\n\t\treturn session;\n\t};\n\tconst deps: PluginBootstrapDepsV1 = {\n\t\tlistActive: async ({ signal }) => {\n\t\t\tconst globalVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.agentDir, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"global\",\n\t\t\t\tDEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst projectVersions = readActiveVersions(\n\t\t\t\tcreatePluginStateStoreV1({ filePath: join(options.cwd, CONFIG_DIR_NAME, DEFAULT_PLUGIN_STATE_FILE_V1) }),\n\t\t\t\t\"project\",\n\t\t\t\tprojectPluginScopeRefV1(options.cwd),\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tconst [globalResult, projectResult] = await Promise.all([globalVersions, projectVersions]);\n\t\t\t// Project precedence mirrors active-binding resolution: a project\n\t\t\t// installation shadows the same plugin id from the global scope.\n\t\t\treturn new Map([...globalResult, ...projectResult]);\n\t\t},\n\t\tinstallFromNpm: async ({ npmName, range, signal }) => {\n\t\t\tconst active = await ensureSession();\n\t\t\t// npm refs only (§0.7): scoped or plain names with the shared minimal\n\t\t\t// range subset; no github/gitee/url/local ref can be expressed here.\n\t\t\tconst ref = range === undefined ? npmName : `${npmName}@${range}`;\n\t\t\tconst acquired = await active.acquisition.resolvePackage({ ref, ...(signal === undefined ? {} : { signal }) });\n\t\t\tlet released = false;\n\t\t\tconst release = async (): Promise<void> => {\n\t\t\t\tif (released) return;\n\t\t\t\treleased = true;\n\t\t\t\tawait acquired.release();\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst verified = active.installer.prepare({\n\t\t\t\t\tmanifest: acquired.manifest,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\thost: defaultPluginHostFactsV1(),\n\t\t\t\t});\n\t\t\t\tif (verified.status !== \"verified\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"installer prepare rejected {pluginId}: {reason}\", {\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t\treason: verified.reason,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst installed = await active.installer.install({\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\tartifact: acquired.artifact,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (installed.status !== \"succeeded\")\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin install failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\t\tcode: installed.error.code,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\tconst activated = await active.installer.activate({\n\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\trecord: verified.record,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (activated.status !== \"succeeded\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: installed.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin activation failed ({code}) for {pluginId}\", {\n\t\t\t\t\t\t\tcode: activated.error.code,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst persisted = await active.stateStore.upsert({\n\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\tscope: \"global\",\n\t\t\t\t\tenabled: true,\n\t\t\t\t\tsource: acquired.source,\n\t\t\t\t\t...(signal === undefined ? {} : { signal }),\n\t\t\t\t});\n\t\t\t\tif (persisted.status !== \"ok\") {\n\t\t\t\t\tawait active.installer\n\t\t\t\t\t\t.remove({\n\t\t\t\t\t\t\tinstallation: activated.installation,\n\t\t\t\t\t\t\tscope: \"global\",\n\t\t\t\t\t\t\tscopeRef: DEFAULT_GLOBAL_PLUGIN_SCOPE_REF_V1,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\tt(\"plugin state persist failed ({status}) for {pluginId}\", {\n\t\t\t\t\t\t\tstatus: persisted.status,\n\t\t\t\t\t\t\tpluginId: acquired.manifest.id,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn { pluginVersion: activated.installation.pluginVersion, source: acquired.source };\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t},\n\t\t...(options.signal === undefined ? {} : { signal: options.signal }),\n\t};\n\ttry {\n\t\treturn await runPluginBootstrapV1({\n\t\t\tmanifest: options.manifest ?? BOOTSTRAP_FACTORY_PLUGIN_MANIFEST_V1,\n\t\t\tdeps,\n\t\t\t// First-run progress line (设计 §4.4, 2026-10-03): a fresh install is\n\t\t\t// otherwise silent for the whole network install window.\n\t\t\tonPendingInstalls:\n\t\t\t\toptions.onPendingInstalls ??\n\t\t\t\t((count: number) => {\n\t\t\t\t\tconsole.error(t(\"First run: installing {count} factory plugins…\", { count }));\n\t\t\t\t}),\n\t\t});\n\t} finally {\n\t\tconst active = session;\n\t\tsession = undefined;\n\t\tif (active !== undefined) {\n\t\t\tawait active.installHost.close().catch(() => undefined);\n\t\t\tawait active.acquisition.close().catch(() => undefined);\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"node-plugin-trust-root.d.ts","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-trust-root.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,8BAA8B,EAC9B,8CAA8C,EAC9C,yCAAyC,EACzC,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,iCAAiC,GAAa,CAAC;AAM5D,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,IAAI,EAAE,SAAS,wBAAwB,EAAE,CAAC;IACnD,4EAA4E;IAC5E,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,qBAAqB,GAAG,CACnC,KAAK,EAAE,8CAA8C,KACjD,yCAAyC,CAAC;AAsD/C,wEAAwE;AACxE,wBAAgB,6CAA6C,CAAC,KAAK,EAAE,8BAA8B,GAAG,UAAU,CAE/G;AAmBD;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,4BAA4B,GAAG,qBAAqB,CA4CxG","sourcesContent":["import { createPublicKey, type KeyObject, verify } from \"node:crypto\";\nimport type {\n\tPluginDistributionDescriptorV1,\n\tPluginDistributionSignatureVerificationInputV1,\n\tPluginDistributionSignatureVerificationV1,\n} from \"./distribution.ts\";\n\nexport const NODE_PLUGIN_TRUST_ROOT_VERSION_V1 = 1 as const;\n\nconst MAX_KEY_ID_CHARS_V1 = 128;\nconst MAX_KEYS_V1 = 256;\nconst SIGNATURE_PREFIX_V1 = \"ed25519-v1\";\n\nexport interface NodePluginTrustRootKeyV1 {\n\treadonly keyId: string;\n\t/** Host-owned SPKI PEM. Private keys are never accepted by this adapter. */\n\treadonly publicKeyPem: string;\n\treadonly notBefore?: string;\n\treadonly notAfter?: string;\n}\n\nexport interface NodePluginTrustRootOptionsV1 {\n\treadonly keys: readonly NodePluginTrustRootKeyV1[];\n\t/** Host-owned revocation set; revoked keys never verify new descriptors. */\n\treadonly revokedKeyIds?: readonly string[];\n\t/** Injected clock for deterministic key validity tests. */\n\treadonly now?: () => number;\n}\n\nexport type NodePluginTrustRootV1 = (\n\tinput: PluginDistributionSignatureVerificationInputV1,\n) => PluginDistributionSignatureVerificationV1;\n\ninterface TrustedKeyV1 {\n\treadonly keyId: string;\n\treadonly publicKey: KeyObject;\n\treadonly notBefore?: number;\n\treadonly notAfter?: number;\n}\n\nfunction validKeyId(value: string): boolean {\n\treturn value.length > 0 && value.length <= MAX_KEY_ID_CHARS_V1 && /^[A-Za-z0-9_-]+$/u.test(value);\n}\n\nfunction parseValidity(value: string | undefined, label: string): number | undefined {\n\tif (value === undefined) return undefined;\n\tif (!/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?Z$/u.test(value))\n\t\tthrow new Error(`${label} must be an ISO-8601 UTC timestamp`);\n\tconst timestamp = Date.parse(value);\n\tif (!Number.isFinite(timestamp) || new Date(timestamp).toISOString() !== value)\n\t\tthrow new Error(`${label} must be a valid ISO-8601 UTC timestamp`);\n\treturn timestamp;\n}\n\nfunction parsePublicKey(value: string): KeyObject {\n\tif (value.length === 0 || value.length > 16 * 1024 || /\\u0000/u.test(value))\n\t\tthrow new Error(\"publicKeyPem is invalid\");\n\tlet key: KeyObject;\n\ttry {\n\t\tkey = createPublicKey(value);\n\t} catch {\n\t\tthrow new Error(\"publicKeyPem is invalid\");\n\t}\n\tif (key.type !== \"public\" || key.asymmetricKeyType !== \"ed25519\")\n\t\tthrow new Error(\"publicKeyPem must be an Ed25519 public key\");\n\treturn key;\n}\n\nfunction payload(input: PluginDistributionDescriptorV1): Buffer {\n\treturn Buffer.from(\n\t\t`agent-forge-plugin-distribution-v${NODE_PLUGIN_TRUST_ROOT_VERSION_V1}\\n${JSON.stringify([\n\t\t\tinput.version,\n\t\t\tinput.source,\n\t\t\tinput.pluginId,\n\t\t\tinput.pluginVersion,\n\t\t\tinput.manifestDigest,\n\t\t\tinput.artifactDigest,\n\t\t\tinput.expiresAt,\n\t\t\tinput.sizeBytes,\n\t\t\tinput.mediaType,\n\t\t])}`,\n\t\t\"utf8\",\n\t);\n}\n\n/** Return the exact bytes signed by `createNodePluginTrustRootV1()`. */\nexport function buildNodePluginDistributionSignaturePayloadV1(input: PluginDistributionDescriptorV1): Uint8Array {\n\treturn new Uint8Array(payload(input));\n}\n\nfunction parseSignature(value: string): { readonly keyId: string; readonly bytes: Buffer } | undefined {\n\tconst parts = value.split(\".\");\n\tif (parts.length !== 3 || parts[0] !== SIGNATURE_PREFIX_V1 || !validKeyId(parts[1] ?? \"\")) return undefined;\n\tconst encoded = parts[2] ?? \"\";\n\tif (!/^[A-Za-z0-9_-]+$/u.test(encoded)) return undefined;\n\tconst bytes = Buffer.from(encoded, \"base64url\");\n\tif (bytes.byteLength !== 64) return undefined;\n\treturn { keyId: parts[1]!, bytes };\n}\n\nfunction validAt(key: TrustedKeyV1, current: number): boolean {\n\treturn (\n\t\t(key.notBefore === undefined || current >= key.notBefore) &&\n\t\t(key.notAfter === undefined || current < key.notAfter)\n\t);\n}\n\n/**\n * Build an explicit Ed25519 trust-root verifier for distribution descriptors.\n * It is a host policy adapter: no key is loaded implicitly and an unknown key\n * or malformed signature is rejected without leaking cryptographic details.\n */\nexport function createNodePluginTrustRootV1(options: NodePluginTrustRootOptionsV1): NodePluginTrustRootV1 {\n\tif (!Array.isArray(options.keys) || options.keys.length === 0 || options.keys.length > MAX_KEYS_V1)\n\t\tthrow new Error(`keys must contain between 1 and ${MAX_KEYS_V1} entries`);\n\tconst revokedKeyIds = new Set(options.revokedKeyIds ?? []);\n\tif (\n\t\t[...revokedKeyIds].some((keyId) => !validKeyId(keyId)) ||\n\t\t(options.revokedKeyIds !== undefined && revokedKeyIds.size !== options.revokedKeyIds.length)\n\t)\n\t\tthrow new Error(\"revokedKeyIds is invalid or duplicated\");\n\tconst keys = new Map<string, TrustedKeyV1>();\n\tfor (const input of options.keys) {\n\t\tif (!validKeyId(input.keyId) || keys.has(input.keyId)) throw new Error(\"keyId is invalid or duplicated\");\n\t\tconst notBefore = parseValidity(input.notBefore, \"notBefore\");\n\t\tconst notAfter = parseValidity(input.notAfter, \"notAfter\");\n\t\tif (notBefore !== undefined && notAfter !== undefined && notBefore >= notAfter)\n\t\t\tthrow new Error(\"notBefore must be earlier than notAfter\");\n\t\tkeys.set(input.keyId, {\n\t\t\tkeyId: input.keyId,\n\t\t\tpublicKey: parsePublicKey(input.publicKeyPem),\n\t\t\t...(notBefore === undefined ? {} : { notBefore }),\n\t\t\t...(notAfter === undefined ? {} : { notAfter }),\n\t\t});\n\t}\n\tconst now = options.now ?? (() => Date.now());\n\treturn ({ descriptor }): PluginDistributionSignatureVerificationV1 => {\n\t\tconst signature = parseSignature(descriptor.signature);\n\t\tif (signature === undefined) return { status: \"rejected\" };\n\t\tconst key = keys.get(signature.keyId);\n\t\tif (key === undefined || revokedKeyIds.has(signature.keyId)) return { status: \"rejected\" };\n\t\tlet current: number;\n\t\ttry {\n\t\t\tcurrent = now();\n\t\t} catch {\n\t\t\treturn { status: \"rejected\" };\n\t\t}\n\t\tif (!Number.isFinite(current) || !validAt(key, current)) return { status: \"rejected\" };\n\t\ttry {\n\t\t\treturn verify(null, payload(descriptor), key.publicKey, signature.bytes)\n\t\t\t\t? { status: \"accepted\" }\n\t\t\t\t: { status: \"rejected\" };\n\t\t} catch {\n\t\t\treturn { status: \"rejected\" };\n\t\t}\n\t};\n}\n"]}
1
+ {"version":3,"file":"node-plugin-trust-root.d.ts","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-trust-root.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,8BAA8B,EAC9B,8CAA8C,EAC9C,yCAAyC,EACzC,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,iCAAiC,GAAa,CAAC;AAM5D,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,IAAI,EAAE,SAAS,wBAAwB,EAAE,CAAC;IACnD,4EAA4E;IAC5E,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,qBAAqB,GAAG,CACnC,KAAK,EAAE,8CAA8C,KACjD,yCAAyC,CAAC;AAsD/C,wEAAwE;AACxE,wBAAgB,6CAA6C,CAAC,KAAK,EAAE,8BAA8B,GAAG,UAAU,CAE/G;AAmBD;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,4BAA4B,GAAG,qBAAqB,CA4CxG","sourcesContent":["import { createPublicKey, type KeyObject, verify } from \"node:crypto\";\n\nimport { t } from \"../../core/i18n/index.ts\";\nimport type {\n\tPluginDistributionDescriptorV1,\n\tPluginDistributionSignatureVerificationInputV1,\n\tPluginDistributionSignatureVerificationV1,\n} from \"./distribution.ts\";\n\nexport const NODE_PLUGIN_TRUST_ROOT_VERSION_V1 = 1 as const;\n\nconst MAX_KEY_ID_CHARS_V1 = 128;\nconst MAX_KEYS_V1 = 256;\nconst SIGNATURE_PREFIX_V1 = \"ed25519-v1\";\n\nexport interface NodePluginTrustRootKeyV1 {\n\treadonly keyId: string;\n\t/** Host-owned SPKI PEM. Private keys are never accepted by this adapter. */\n\treadonly publicKeyPem: string;\n\treadonly notBefore?: string;\n\treadonly notAfter?: string;\n}\n\nexport interface NodePluginTrustRootOptionsV1 {\n\treadonly keys: readonly NodePluginTrustRootKeyV1[];\n\t/** Host-owned revocation set; revoked keys never verify new descriptors. */\n\treadonly revokedKeyIds?: readonly string[];\n\t/** Injected clock for deterministic key validity tests. */\n\treadonly now?: () => number;\n}\n\nexport type NodePluginTrustRootV1 = (\n\tinput: PluginDistributionSignatureVerificationInputV1,\n) => PluginDistributionSignatureVerificationV1;\n\ninterface TrustedKeyV1 {\n\treadonly keyId: string;\n\treadonly publicKey: KeyObject;\n\treadonly notBefore?: number;\n\treadonly notAfter?: number;\n}\n\nfunction validKeyId(value: string): boolean {\n\treturn value.length > 0 && value.length <= MAX_KEY_ID_CHARS_V1 && /^[A-Za-z0-9_-]+$/u.test(value);\n}\n\nfunction parseValidity(value: string | undefined, label: string): number | undefined {\n\tif (value === undefined) return undefined;\n\tif (!/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?Z$/u.test(value))\n\t\tthrow new Error(t(\"{label} must be an ISO-8601 UTC timestamp\", { label }));\n\tconst timestamp = Date.parse(value);\n\tif (!Number.isFinite(timestamp) || new Date(timestamp).toISOString() !== value)\n\t\tthrow new Error(t(\"{label} must be a valid ISO-8601 UTC timestamp\", { label }));\n\treturn timestamp;\n}\n\nfunction parsePublicKey(value: string): KeyObject {\n\tif (value.length === 0 || value.length > 16 * 1024 || /\\u0000/u.test(value))\n\t\tthrow new Error(t(\"publicKeyPem is invalid\"));\n\tlet key: KeyObject;\n\ttry {\n\t\tkey = createPublicKey(value);\n\t} catch {\n\t\tthrow new Error(t(\"publicKeyPem is invalid\"));\n\t}\n\tif (key.type !== \"public\" || key.asymmetricKeyType !== \"ed25519\")\n\t\tthrow new Error(t(\"publicKeyPem must be an Ed25519 public key\"));\n\treturn key;\n}\n\nfunction payload(input: PluginDistributionDescriptorV1): Buffer {\n\treturn Buffer.from(\n\t\t`agent-forge-plugin-distribution-v${NODE_PLUGIN_TRUST_ROOT_VERSION_V1}\\n${JSON.stringify([\n\t\t\tinput.version,\n\t\t\tinput.source,\n\t\t\tinput.pluginId,\n\t\t\tinput.pluginVersion,\n\t\t\tinput.manifestDigest,\n\t\t\tinput.artifactDigest,\n\t\t\tinput.expiresAt,\n\t\t\tinput.sizeBytes,\n\t\t\tinput.mediaType,\n\t\t])}`,\n\t\t\"utf8\",\n\t);\n}\n\n/** Return the exact bytes signed by `createNodePluginTrustRootV1()`. */\nexport function buildNodePluginDistributionSignaturePayloadV1(input: PluginDistributionDescriptorV1): Uint8Array {\n\treturn new Uint8Array(payload(input));\n}\n\nfunction parseSignature(value: string): { readonly keyId: string; readonly bytes: Buffer } | undefined {\n\tconst parts = value.split(\".\");\n\tif (parts.length !== 3 || parts[0] !== SIGNATURE_PREFIX_V1 || !validKeyId(parts[1] ?? \"\")) return undefined;\n\tconst encoded = parts[2] ?? \"\";\n\tif (!/^[A-Za-z0-9_-]+$/u.test(encoded)) return undefined;\n\tconst bytes = Buffer.from(encoded, \"base64url\");\n\tif (bytes.byteLength !== 64) return undefined;\n\treturn { keyId: parts[1]!, bytes };\n}\n\nfunction validAt(key: TrustedKeyV1, current: number): boolean {\n\treturn (\n\t\t(key.notBefore === undefined || current >= key.notBefore) &&\n\t\t(key.notAfter === undefined || current < key.notAfter)\n\t);\n}\n\n/**\n * Build an explicit Ed25519 trust-root verifier for distribution descriptors.\n * It is a host policy adapter: no key is loaded implicitly and an unknown key\n * or malformed signature is rejected without leaking cryptographic details.\n */\nexport function createNodePluginTrustRootV1(options: NodePluginTrustRootOptionsV1): NodePluginTrustRootV1 {\n\tif (!Array.isArray(options.keys) || options.keys.length === 0 || options.keys.length > MAX_KEYS_V1)\n\t\tthrow new Error(t(\"keys must contain between 1 and {max} entries\", { max: MAX_KEYS_V1 }));\n\tconst revokedKeyIds = new Set(options.revokedKeyIds ?? []);\n\tif (\n\t\t[...revokedKeyIds].some((keyId) => !validKeyId(keyId)) ||\n\t\t(options.revokedKeyIds !== undefined && revokedKeyIds.size !== options.revokedKeyIds.length)\n\t)\n\t\tthrow new Error(t(\"revokedKeyIds is invalid or duplicated\"));\n\tconst keys = new Map<string, TrustedKeyV1>();\n\tfor (const input of options.keys) {\n\t\tif (!validKeyId(input.keyId) || keys.has(input.keyId)) throw new Error(t(\"keyId is invalid or duplicated\"));\n\t\tconst notBefore = parseValidity(input.notBefore, \"notBefore\");\n\t\tconst notAfter = parseValidity(input.notAfter, \"notAfter\");\n\t\tif (notBefore !== undefined && notAfter !== undefined && notBefore >= notAfter)\n\t\t\tthrow new Error(t(\"notBefore must be earlier than notAfter\"));\n\t\tkeys.set(input.keyId, {\n\t\t\tkeyId: input.keyId,\n\t\t\tpublicKey: parsePublicKey(input.publicKeyPem),\n\t\t\t...(notBefore === undefined ? {} : { notBefore }),\n\t\t\t...(notAfter === undefined ? {} : { notAfter }),\n\t\t});\n\t}\n\tconst now = options.now ?? (() => Date.now());\n\treturn ({ descriptor }): PluginDistributionSignatureVerificationV1 => {\n\t\tconst signature = parseSignature(descriptor.signature);\n\t\tif (signature === undefined) return { status: \"rejected\" };\n\t\tconst key = keys.get(signature.keyId);\n\t\tif (key === undefined || revokedKeyIds.has(signature.keyId)) return { status: \"rejected\" };\n\t\tlet current: number;\n\t\ttry {\n\t\t\tcurrent = now();\n\t\t} catch {\n\t\t\treturn { status: \"rejected\" };\n\t\t}\n\t\tif (!Number.isFinite(current) || !validAt(key, current)) return { status: \"rejected\" };\n\t\ttry {\n\t\t\treturn verify(null, payload(descriptor), key.publicKey, signature.bytes)\n\t\t\t\t? { status: \"accepted\" }\n\t\t\t\t: { status: \"rejected\" };\n\t\t} catch {\n\t\t\treturn { status: \"rejected\" };\n\t\t}\n\t};\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { createPublicKey, verify } from "node:crypto";
2
+ import { t } from "../../core/i18n/index.js";
2
3
  export const NODE_PLUGIN_TRUST_ROOT_VERSION_V1 = 1;
3
4
  const MAX_KEY_ID_CHARS_V1 = 128;
4
5
  const MAX_KEYS_V1 = 256;
@@ -10,24 +11,24 @@ function parseValidity(value, label) {
10
11
  if (value === undefined)
11
12
  return undefined;
12
13
  if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/u.test(value))
13
- throw new Error(`${label} must be an ISO-8601 UTC timestamp`);
14
+ throw new Error(t("{label} must be an ISO-8601 UTC timestamp", { label }));
14
15
  const timestamp = Date.parse(value);
15
16
  if (!Number.isFinite(timestamp) || new Date(timestamp).toISOString() !== value)
16
- throw new Error(`${label} must be a valid ISO-8601 UTC timestamp`);
17
+ throw new Error(t("{label} must be a valid ISO-8601 UTC timestamp", { label }));
17
18
  return timestamp;
18
19
  }
19
20
  function parsePublicKey(value) {
20
21
  if (value.length === 0 || value.length > 16 * 1024 || /\u0000/u.test(value))
21
- throw new Error("publicKeyPem is invalid");
22
+ throw new Error(t("publicKeyPem is invalid"));
22
23
  let key;
23
24
  try {
24
25
  key = createPublicKey(value);
25
26
  }
26
27
  catch {
27
- throw new Error("publicKeyPem is invalid");
28
+ throw new Error(t("publicKeyPem is invalid"));
28
29
  }
29
30
  if (key.type !== "public" || key.asymmetricKeyType !== "ed25519")
30
- throw new Error("publicKeyPem must be an Ed25519 public key");
31
+ throw new Error(t("publicKeyPem must be an Ed25519 public key"));
31
32
  return key;
32
33
  }
33
34
  function payload(input) {
@@ -70,19 +71,19 @@ function validAt(key, current) {
70
71
  */
71
72
  export function createNodePluginTrustRootV1(options) {
72
73
  if (!Array.isArray(options.keys) || options.keys.length === 0 || options.keys.length > MAX_KEYS_V1)
73
- throw new Error(`keys must contain between 1 and ${MAX_KEYS_V1} entries`);
74
+ throw new Error(t("keys must contain between 1 and {max} entries", { max: MAX_KEYS_V1 }));
74
75
  const revokedKeyIds = new Set(options.revokedKeyIds ?? []);
75
76
  if ([...revokedKeyIds].some((keyId) => !validKeyId(keyId)) ||
76
77
  (options.revokedKeyIds !== undefined && revokedKeyIds.size !== options.revokedKeyIds.length))
77
- throw new Error("revokedKeyIds is invalid or duplicated");
78
+ throw new Error(t("revokedKeyIds is invalid or duplicated"));
78
79
  const keys = new Map();
79
80
  for (const input of options.keys) {
80
81
  if (!validKeyId(input.keyId) || keys.has(input.keyId))
81
- throw new Error("keyId is invalid or duplicated");
82
+ throw new Error(t("keyId is invalid or duplicated"));
82
83
  const notBefore = parseValidity(input.notBefore, "notBefore");
83
84
  const notAfter = parseValidity(input.notAfter, "notAfter");
84
85
  if (notBefore !== undefined && notAfter !== undefined && notBefore >= notAfter)
85
- throw new Error("notBefore must be earlier than notAfter");
86
+ throw new Error(t("notBefore must be earlier than notAfter"));
86
87
  keys.set(input.keyId, {
87
88
  keyId: input.keyId,
88
89
  publicKey: parsePublicKey(input.publicKeyPem),
@@ -1 +1 @@
1
- {"version":3,"file":"node-plugin-trust-root.js","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-trust-root.ts"],"names":[],"mappings":"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","sourcesContent":["import { createPublicKey, type KeyObject, verify } from \"node:crypto\";\nimport type {\n\tPluginDistributionDescriptorV1,\n\tPluginDistributionSignatureVerificationInputV1,\n\tPluginDistributionSignatureVerificationV1,\n} from \"./distribution.ts\";\n\nexport const NODE_PLUGIN_TRUST_ROOT_VERSION_V1 = 1 as const;\n\nconst MAX_KEY_ID_CHARS_V1 = 128;\nconst MAX_KEYS_V1 = 256;\nconst SIGNATURE_PREFIX_V1 = \"ed25519-v1\";\n\nexport interface NodePluginTrustRootKeyV1 {\n\treadonly keyId: string;\n\t/** Host-owned SPKI PEM. Private keys are never accepted by this adapter. */\n\treadonly publicKeyPem: string;\n\treadonly notBefore?: string;\n\treadonly notAfter?: string;\n}\n\nexport interface NodePluginTrustRootOptionsV1 {\n\treadonly keys: readonly NodePluginTrustRootKeyV1[];\n\t/** Host-owned revocation set; revoked keys never verify new descriptors. */\n\treadonly revokedKeyIds?: readonly string[];\n\t/** Injected clock for deterministic key validity tests. */\n\treadonly now?: () => number;\n}\n\nexport type NodePluginTrustRootV1 = (\n\tinput: PluginDistributionSignatureVerificationInputV1,\n) => PluginDistributionSignatureVerificationV1;\n\ninterface TrustedKeyV1 {\n\treadonly keyId: string;\n\treadonly publicKey: KeyObject;\n\treadonly notBefore?: number;\n\treadonly notAfter?: number;\n}\n\nfunction validKeyId(value: string): boolean {\n\treturn value.length > 0 && value.length <= MAX_KEY_ID_CHARS_V1 && /^[A-Za-z0-9_-]+$/u.test(value);\n}\n\nfunction parseValidity(value: string | undefined, label: string): number | undefined {\n\tif (value === undefined) return undefined;\n\tif (!/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?Z$/u.test(value))\n\t\tthrow new Error(`${label} must be an ISO-8601 UTC timestamp`);\n\tconst timestamp = Date.parse(value);\n\tif (!Number.isFinite(timestamp) || new Date(timestamp).toISOString() !== value)\n\t\tthrow new Error(`${label} must be a valid ISO-8601 UTC timestamp`);\n\treturn timestamp;\n}\n\nfunction parsePublicKey(value: string): KeyObject {\n\tif (value.length === 0 || value.length > 16 * 1024 || /\\u0000/u.test(value))\n\t\tthrow new Error(\"publicKeyPem is invalid\");\n\tlet key: KeyObject;\n\ttry {\n\t\tkey = createPublicKey(value);\n\t} catch {\n\t\tthrow new Error(\"publicKeyPem is invalid\");\n\t}\n\tif (key.type !== \"public\" || key.asymmetricKeyType !== \"ed25519\")\n\t\tthrow new Error(\"publicKeyPem must be an Ed25519 public key\");\n\treturn key;\n}\n\nfunction payload(input: PluginDistributionDescriptorV1): Buffer {\n\treturn Buffer.from(\n\t\t`agent-forge-plugin-distribution-v${NODE_PLUGIN_TRUST_ROOT_VERSION_V1}\\n${JSON.stringify([\n\t\t\tinput.version,\n\t\t\tinput.source,\n\t\t\tinput.pluginId,\n\t\t\tinput.pluginVersion,\n\t\t\tinput.manifestDigest,\n\t\t\tinput.artifactDigest,\n\t\t\tinput.expiresAt,\n\t\t\tinput.sizeBytes,\n\t\t\tinput.mediaType,\n\t\t])}`,\n\t\t\"utf8\",\n\t);\n}\n\n/** Return the exact bytes signed by `createNodePluginTrustRootV1()`. */\nexport function buildNodePluginDistributionSignaturePayloadV1(input: PluginDistributionDescriptorV1): Uint8Array {\n\treturn new Uint8Array(payload(input));\n}\n\nfunction parseSignature(value: string): { readonly keyId: string; readonly bytes: Buffer } | undefined {\n\tconst parts = value.split(\".\");\n\tif (parts.length !== 3 || parts[0] !== SIGNATURE_PREFIX_V1 || !validKeyId(parts[1] ?? \"\")) return undefined;\n\tconst encoded = parts[2] ?? \"\";\n\tif (!/^[A-Za-z0-9_-]+$/u.test(encoded)) return undefined;\n\tconst bytes = Buffer.from(encoded, \"base64url\");\n\tif (bytes.byteLength !== 64) return undefined;\n\treturn { keyId: parts[1]!, bytes };\n}\n\nfunction validAt(key: TrustedKeyV1, current: number): boolean {\n\treturn (\n\t\t(key.notBefore === undefined || current >= key.notBefore) &&\n\t\t(key.notAfter === undefined || current < key.notAfter)\n\t);\n}\n\n/**\n * Build an explicit Ed25519 trust-root verifier for distribution descriptors.\n * It is a host policy adapter: no key is loaded implicitly and an unknown key\n * or malformed signature is rejected without leaking cryptographic details.\n */\nexport function createNodePluginTrustRootV1(options: NodePluginTrustRootOptionsV1): NodePluginTrustRootV1 {\n\tif (!Array.isArray(options.keys) || options.keys.length === 0 || options.keys.length > MAX_KEYS_V1)\n\t\tthrow new Error(`keys must contain between 1 and ${MAX_KEYS_V1} entries`);\n\tconst revokedKeyIds = new Set(options.revokedKeyIds ?? []);\n\tif (\n\t\t[...revokedKeyIds].some((keyId) => !validKeyId(keyId)) ||\n\t\t(options.revokedKeyIds !== undefined && revokedKeyIds.size !== options.revokedKeyIds.length)\n\t)\n\t\tthrow new Error(\"revokedKeyIds is invalid or duplicated\");\n\tconst keys = new Map<string, TrustedKeyV1>();\n\tfor (const input of options.keys) {\n\t\tif (!validKeyId(input.keyId) || keys.has(input.keyId)) throw new Error(\"keyId is invalid or duplicated\");\n\t\tconst notBefore = parseValidity(input.notBefore, \"notBefore\");\n\t\tconst notAfter = parseValidity(input.notAfter, \"notAfter\");\n\t\tif (notBefore !== undefined && notAfter !== undefined && notBefore >= notAfter)\n\t\t\tthrow new Error(\"notBefore must be earlier than notAfter\");\n\t\tkeys.set(input.keyId, {\n\t\t\tkeyId: input.keyId,\n\t\t\tpublicKey: parsePublicKey(input.publicKeyPem),\n\t\t\t...(notBefore === undefined ? {} : { notBefore }),\n\t\t\t...(notAfter === undefined ? {} : { notAfter }),\n\t\t});\n\t}\n\tconst now = options.now ?? (() => Date.now());\n\treturn ({ descriptor }): PluginDistributionSignatureVerificationV1 => {\n\t\tconst signature = parseSignature(descriptor.signature);\n\t\tif (signature === undefined) return { status: \"rejected\" };\n\t\tconst key = keys.get(signature.keyId);\n\t\tif (key === undefined || revokedKeyIds.has(signature.keyId)) return { status: \"rejected\" };\n\t\tlet current: number;\n\t\ttry {\n\t\t\tcurrent = now();\n\t\t} catch {\n\t\t\treturn { status: \"rejected\" };\n\t\t}\n\t\tif (!Number.isFinite(current) || !validAt(key, current)) return { status: \"rejected\" };\n\t\ttry {\n\t\t\treturn verify(null, payload(descriptor), key.publicKey, signature.bytes)\n\t\t\t\t? { status: \"accepted\" }\n\t\t\t\t: { status: \"rejected\" };\n\t\t} catch {\n\t\t\treturn { status: \"rejected\" };\n\t\t}\n\t};\n}\n"]}
1
+ {"version":3,"file":"node-plugin-trust-root.js","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-plugin-trust-root.ts"],"names":[],"mappings":"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","sourcesContent":["import { createPublicKey, type KeyObject, verify } from \"node:crypto\";\n\nimport { t } from \"../../core/i18n/index.ts\";\nimport type {\n\tPluginDistributionDescriptorV1,\n\tPluginDistributionSignatureVerificationInputV1,\n\tPluginDistributionSignatureVerificationV1,\n} from \"./distribution.ts\";\n\nexport const NODE_PLUGIN_TRUST_ROOT_VERSION_V1 = 1 as const;\n\nconst MAX_KEY_ID_CHARS_V1 = 128;\nconst MAX_KEYS_V1 = 256;\nconst SIGNATURE_PREFIX_V1 = \"ed25519-v1\";\n\nexport interface NodePluginTrustRootKeyV1 {\n\treadonly keyId: string;\n\t/** Host-owned SPKI PEM. Private keys are never accepted by this adapter. */\n\treadonly publicKeyPem: string;\n\treadonly notBefore?: string;\n\treadonly notAfter?: string;\n}\n\nexport interface NodePluginTrustRootOptionsV1 {\n\treadonly keys: readonly NodePluginTrustRootKeyV1[];\n\t/** Host-owned revocation set; revoked keys never verify new descriptors. */\n\treadonly revokedKeyIds?: readonly string[];\n\t/** Injected clock for deterministic key validity tests. */\n\treadonly now?: () => number;\n}\n\nexport type NodePluginTrustRootV1 = (\n\tinput: PluginDistributionSignatureVerificationInputV1,\n) => PluginDistributionSignatureVerificationV1;\n\ninterface TrustedKeyV1 {\n\treadonly keyId: string;\n\treadonly publicKey: KeyObject;\n\treadonly notBefore?: number;\n\treadonly notAfter?: number;\n}\n\nfunction validKeyId(value: string): boolean {\n\treturn value.length > 0 && value.length <= MAX_KEY_ID_CHARS_V1 && /^[A-Za-z0-9_-]+$/u.test(value);\n}\n\nfunction parseValidity(value: string | undefined, label: string): number | undefined {\n\tif (value === undefined) return undefined;\n\tif (!/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?Z$/u.test(value))\n\t\tthrow new Error(t(\"{label} must be an ISO-8601 UTC timestamp\", { label }));\n\tconst timestamp = Date.parse(value);\n\tif (!Number.isFinite(timestamp) || new Date(timestamp).toISOString() !== value)\n\t\tthrow new Error(t(\"{label} must be a valid ISO-8601 UTC timestamp\", { label }));\n\treturn timestamp;\n}\n\nfunction parsePublicKey(value: string): KeyObject {\n\tif (value.length === 0 || value.length > 16 * 1024 || /\\u0000/u.test(value))\n\t\tthrow new Error(t(\"publicKeyPem is invalid\"));\n\tlet key: KeyObject;\n\ttry {\n\t\tkey = createPublicKey(value);\n\t} catch {\n\t\tthrow new Error(t(\"publicKeyPem is invalid\"));\n\t}\n\tif (key.type !== \"public\" || key.asymmetricKeyType !== \"ed25519\")\n\t\tthrow new Error(t(\"publicKeyPem must be an Ed25519 public key\"));\n\treturn key;\n}\n\nfunction payload(input: PluginDistributionDescriptorV1): Buffer {\n\treturn Buffer.from(\n\t\t`agent-forge-plugin-distribution-v${NODE_PLUGIN_TRUST_ROOT_VERSION_V1}\\n${JSON.stringify([\n\t\t\tinput.version,\n\t\t\tinput.source,\n\t\t\tinput.pluginId,\n\t\t\tinput.pluginVersion,\n\t\t\tinput.manifestDigest,\n\t\t\tinput.artifactDigest,\n\t\t\tinput.expiresAt,\n\t\t\tinput.sizeBytes,\n\t\t\tinput.mediaType,\n\t\t])}`,\n\t\t\"utf8\",\n\t);\n}\n\n/** Return the exact bytes signed by `createNodePluginTrustRootV1()`. */\nexport function buildNodePluginDistributionSignaturePayloadV1(input: PluginDistributionDescriptorV1): Uint8Array {\n\treturn new Uint8Array(payload(input));\n}\n\nfunction parseSignature(value: string): { readonly keyId: string; readonly bytes: Buffer } | undefined {\n\tconst parts = value.split(\".\");\n\tif (parts.length !== 3 || parts[0] !== SIGNATURE_PREFIX_V1 || !validKeyId(parts[1] ?? \"\")) return undefined;\n\tconst encoded = parts[2] ?? \"\";\n\tif (!/^[A-Za-z0-9_-]+$/u.test(encoded)) return undefined;\n\tconst bytes = Buffer.from(encoded, \"base64url\");\n\tif (bytes.byteLength !== 64) return undefined;\n\treturn { keyId: parts[1]!, bytes };\n}\n\nfunction validAt(key: TrustedKeyV1, current: number): boolean {\n\treturn (\n\t\t(key.notBefore === undefined || current >= key.notBefore) &&\n\t\t(key.notAfter === undefined || current < key.notAfter)\n\t);\n}\n\n/**\n * Build an explicit Ed25519 trust-root verifier for distribution descriptors.\n * It is a host policy adapter: no key is loaded implicitly and an unknown key\n * or malformed signature is rejected without leaking cryptographic details.\n */\nexport function createNodePluginTrustRootV1(options: NodePluginTrustRootOptionsV1): NodePluginTrustRootV1 {\n\tif (!Array.isArray(options.keys) || options.keys.length === 0 || options.keys.length > MAX_KEYS_V1)\n\t\tthrow new Error(t(\"keys must contain between 1 and {max} entries\", { max: MAX_KEYS_V1 }));\n\tconst revokedKeyIds = new Set(options.revokedKeyIds ?? []);\n\tif (\n\t\t[...revokedKeyIds].some((keyId) => !validKeyId(keyId)) ||\n\t\t(options.revokedKeyIds !== undefined && revokedKeyIds.size !== options.revokedKeyIds.length)\n\t)\n\t\tthrow new Error(t(\"revokedKeyIds is invalid or duplicated\"));\n\tconst keys = new Map<string, TrustedKeyV1>();\n\tfor (const input of options.keys) {\n\t\tif (!validKeyId(input.keyId) || keys.has(input.keyId)) throw new Error(t(\"keyId is invalid or duplicated\"));\n\t\tconst notBefore = parseValidity(input.notBefore, \"notBefore\");\n\t\tconst notAfter = parseValidity(input.notAfter, \"notAfter\");\n\t\tif (notBefore !== undefined && notAfter !== undefined && notBefore >= notAfter)\n\t\t\tthrow new Error(t(\"notBefore must be earlier than notAfter\"));\n\t\tkeys.set(input.keyId, {\n\t\t\tkeyId: input.keyId,\n\t\t\tpublicKey: parsePublicKey(input.publicKeyPem),\n\t\t\t...(notBefore === undefined ? {} : { notBefore }),\n\t\t\t...(notAfter === undefined ? {} : { notAfter }),\n\t\t});\n\t}\n\tconst now = options.now ?? (() => Date.now());\n\treturn ({ descriptor }): PluginDistributionSignatureVerificationV1 => {\n\t\tconst signature = parseSignature(descriptor.signature);\n\t\tif (signature === undefined) return { status: \"rejected\" };\n\t\tconst key = keys.get(signature.keyId);\n\t\tif (key === undefined || revokedKeyIds.has(signature.keyId)) return { status: \"rejected\" };\n\t\tlet current: number;\n\t\ttry {\n\t\t\tcurrent = now();\n\t\t} catch {\n\t\t\treturn { status: \"rejected\" };\n\t\t}\n\t\tif (!Number.isFinite(current) || !validAt(key, current)) return { status: \"rejected\" };\n\t\ttry {\n\t\t\treturn verify(null, payload(descriptor), key.publicKey, signature.bytes)\n\t\t\t\t? { status: \"accepted\" }\n\t\t\t\t: { status: \"rejected\" };\n\t\t} catch {\n\t\t\treturn { status: \"rejected\" };\n\t\t}\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"node-registry-source.d.ts","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-registry-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoC,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAE9F,QAAA,MAAM,2CAA2C,GAAa,CAAC;AAM/D,MAAM,MAAM,uCAAuC,GAChD,WAAW,GACX,uBAAuB,GACvB,gBAAgB,GAChB,mBAAmB,GACnB,kBAAkB,GAClB,oBAAoB,CAAC;AAExB;;;GAGG;AACH,qBAAa,mCAAoC,SAAQ,KAAK;IAC7D,QAAQ,CAAC,IAAI,EAAE,uCAAuC,CAAC;IAEvD,YAAY,IAAI,EAAE,uCAAuC,EAAE,OAAO,EAAE,MAAM,EAIzE;CACD;AAED,MAAM,WAAW,qCAAqC;IACrD,wEAAwE;IACxE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IAC3B,gEAAgE;IAChE,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,oDAAoD;IACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACzC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA+B,SAAQ,sBAAsB;IAC7E,QAAQ,CAAC,OAAO,EAAE,OAAO,2CAA2C,CAAC;IACrE,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AA4ND;;;;GAIG;AACH,wBAAgB,oCAAoC,CACnD,OAAO,EAAE,qCAAqC,GAC5C,8BAA8B,CAmGhC","sourcesContent":["import type { PluginRegistryManifestEnvelopeV1, PluginRegistrySourceV1 } from \"./registry.ts\";\n\nconst NODE_HTTP_PLUGIN_REGISTRY_SOURCE_VERSION_V1 = 1 as const;\nconst DEFAULT_TIMEOUT_MS_V1 = 15_000;\nconst MAX_TIMEOUT_MS_V1 = 120_000;\nconst DEFAULT_MAX_RESPONSE_BYTES_V1 = 4 * 1024 * 1024;\nconst MAX_RESPONSE_BYTES_V1 = 8 * 1024 * 1024;\n\nexport type NodeHttpPluginRegistrySourceErrorCodeV1 =\n\t| \"cancelled\"\n\t| \"invalid_configuration\"\n\t| \"request_failed\"\n\t| \"request_timed_out\"\n\t| \"invalid_response\"\n\t| \"response_too_large\";\n\n/**\n * Deliberately does not retain transport exceptions or response bodies: callers\n * must not turn registry diagnostics into a credential or path disclosure.\n */\nexport class NodeHttpPluginRegistrySourceErrorV1 extends Error {\n\treadonly code: NodeHttpPluginRegistrySourceErrorCodeV1;\n\n\tconstructor(code: NodeHttpPluginRegistrySourceErrorCodeV1, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"NodeHttpPluginRegistrySourceErrorV1\";\n\t\tthis.code = code;\n\t}\n}\n\nexport interface NodeHttpPluginRegistrySourceOptionsV1 {\n\t/** Absolute endpoint returning the versioned JSON registry document. */\n\treadonly url: string | URL;\n\t/** Exact http(s) origins the host has intentionally allowed. */\n\treadonly allowedOrigins: readonly string[];\n\t/** Real fetch or deterministic test replacement. */\n\treadonly fetch?: typeof globalThis.fetch;\n\treadonly requestTimeoutMs?: number;\n\treadonly maxResponseBytes?: number;\n\treadonly userAgent?: string;\n}\n\nexport interface NodeHttpPluginRegistrySourceV1 extends PluginRegistrySourceV1 {\n\treadonly version: typeof NODE_HTTP_PLUGIN_REGISTRY_SOURCE_VERSION_V1;\n\t/** Sanitized endpoint identity: never contains credentials, query, or fragment. */\n\treadonly source: string;\n}\n\ninterface RegistryDocumentV1 {\n\treadonly version: 1;\n\treadonly entries: readonly PluginRegistryManifestEnvelopeV1[];\n}\n\nfunction boundedPositive(value: number | undefined, fallback: number, maximum: number, label: string): number {\n\tconst result = value ?? fallback;\n\tif (!Number.isSafeInteger(result) || result <= 0 || result > maximum) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\t`${label} must be a positive integer at most ${maximum}`,\n\t\t);\n\t}\n\treturn result;\n}\n\nfunction allowedOrigin(value: string): string {\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(value);\n\t} catch {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_configuration\", \"Allowed registry origin is invalid\");\n\t}\n\tif ((parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") || parsed.username || parsed.password) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\t\"Allowed registry origin must be an http(s) origin without credentials\",\n\t\t);\n\t}\n\tif (parsed.pathname !== \"/\" || parsed.search !== \"\" || parsed.hash !== \"\") {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\t\"Allowed registry origin must not include a path, query, or fragment\",\n\t\t);\n\t}\n\treturn parsed.origin;\n}\n\nfunction endpointUrl(value: string | URL, origins: ReadonlySet<string>): URL {\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(value);\n\t} catch {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_configuration\", \"Plugin registry URL is invalid\");\n\t}\n\tif ((parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") || parsed.username || parsed.password) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\t\"Plugin registry URL must use http(s) without credentials\",\n\t\t);\n\t}\n\tif (parsed.search !== \"\" || parsed.hash !== \"\") {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\t\"Plugin registry URL must not include a query or fragment\",\n\t\t);\n\t}\n\tif (!origins.has(parsed.origin)) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_configuration\", \"Plugin registry origin is not allowed\");\n\t}\n\treturn parsed;\n}\n\nfunction responseLength(response: Response): number | undefined {\n\tconst raw = response.headers.get(\"content-length\");\n\tif (raw === null) return undefined;\n\tif (!/^\\d+$/u.test(raw)) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_response\", \"Plugin registry response length is invalid\");\n\t}\n\tconst value = Number(raw);\n\tif (!Number.isSafeInteger(value)) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_response\", \"Plugin registry response length is invalid\");\n\t}\n\treturn value;\n}\n\nfunction isJsonResponse(response: Response): boolean {\n\tconst contentType = response.headers.get(\"content-type\");\n\tif (contentType === null) return false;\n\tconst mediaType = contentType.split(\";\", 1)[0]?.trim().toLowerCase();\n\treturn mediaType === \"application/json\" || mediaType === \"application/vnd.agent-forge.plugin-registry+json\";\n}\n\nfunction requestController(\n\tparent: AbortSignal | undefined,\n\ttimeoutMs: number,\n): {\n\treadonly signal: AbortSignal;\n\treadonly timedOut: () => boolean;\n\treadonly cleanup: () => void;\n} {\n\tconst controller = new AbortController();\n\tlet timedOut = false;\n\tconst abort = () => controller.abort(parent?.reason);\n\tif (parent?.aborted === true) abort();\n\telse parent?.addEventListener(\"abort\", abort, { once: true });\n\tconst timeout = setTimeout(() => {\n\t\ttimedOut = true;\n\t\tcontroller.abort();\n\t}, timeoutMs);\n\treturn {\n\t\tsignal: controller.signal,\n\t\ttimedOut: () => timedOut,\n\t\tcleanup: () => {\n\t\t\tclearTimeout(timeout);\n\t\t\tparent?.removeEventListener(\"abort\", abort);\n\t\t},\n\t};\n}\n\nfunction abortPromise(signal: AbortSignal): { readonly promise: Promise<never>; readonly cleanup: () => void } {\n\tlet rejectAbort: (() => void) | undefined;\n\tconst promise = new Promise<never>((_resolve, reject) => {\n\t\trejectAbort = () => reject(new DOMException(\"aborted\", \"AbortError\"));\n\t\tif (signal.aborted) rejectAbort();\n\t\telse signal.addEventListener(\"abort\", rejectAbort, { once: true });\n\t});\n\treturn {\n\t\tpromise,\n\t\tcleanup: () => {\n\t\t\tif (rejectAbort !== undefined) signal.removeEventListener(\"abort\", rejectAbort);\n\t\t},\n\t};\n}\n\nasync function awaitBeforeAbort<T>(operation: Promise<T>, signal: AbortSignal): Promise<T> {\n\tconst abort = abortPromise(signal);\n\ttry {\n\t\treturn await Promise.race([operation, abort.promise]);\n\t} finally {\n\t\tabort.cleanup();\n\t}\n}\n\nfunction discardResponseBody(response: Response | undefined): void {\n\tconst responseBody = response?.body;\n\tif (responseBody !== undefined && responseBody !== null) {\n\t\tvoid responseBody.cancel().catch(() => undefined);\n\t}\n}\n\nasync function readBody(response: Response, maxBytes: number, signal: AbortSignal): Promise<Uint8Array> {\n\tif (response.body === null) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_response\", \"Plugin registry response has no body\");\n\t}\n\tconst reader = response.body.getReader();\n\tconst chunks: Uint8Array[] = [];\n\tlet total = 0;\n\ttry {\n\t\tfor (;;) {\n\t\t\tif (signal.aborted) throw new DOMException(\"aborted\", \"AbortError\");\n\t\t\tconst item = await awaitBeforeAbort(reader.read(), signal);\n\t\t\tif (item.done) break;\n\t\t\ttotal += item.value.byteLength;\n\t\t\tif (total > maxBytes) {\n\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\t\t\"response_too_large\",\n\t\t\t\t\t\"Plugin registry response exceeds the configured size limit\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tchunks.push(item.value);\n\t\t}\n\t} finally {\n\t\treader.releaseLock();\n\t}\n\tconst bytes = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tbytes.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\treturn bytes;\n}\n\nfunction parseDocument(bytes: Uint8Array): readonly PluginRegistryManifestEnvelopeV1[] {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes));\n\t} catch {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_response\", \"Plugin registry response is not valid JSON\");\n\t}\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_response\", \"Plugin registry document is invalid\");\n\t}\n\tconst document = value as Record<string, unknown>;\n\tif (\n\t\tObject.keys(document).some((key) => key !== \"version\" && key !== \"entries\") ||\n\t\tdocument.version !== 1 ||\n\t\t!Array.isArray(document.entries)\n\t) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_response\", \"Plugin registry document is invalid\");\n\t}\n\treturn document.entries as unknown as RegistryDocumentV1[\"entries\"];\n}\n\nfunction isAbortLike(error: unknown): boolean {\n\treturn error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction isCallerCancelled(signal: AbortSignal | undefined): boolean {\n\treturn signal?.aborted === true;\n}\n\nfunction sanitizedSourceError(error: NodeHttpPluginRegistrySourceErrorV1): NodeHttpPluginRegistrySourceErrorV1 {\n\tconst code: unknown = error.code;\n\tif (\n\t\tcode !== \"cancelled\" &&\n\t\tcode !== \"invalid_configuration\" &&\n\t\tcode !== \"request_failed\" &&\n\t\tcode !== \"request_timed_out\" &&\n\t\tcode !== \"invalid_response\" &&\n\t\tcode !== \"response_too_large\"\n\t) {\n\t\treturn new NodeHttpPluginRegistrySourceErrorV1(\"request_failed\", \"Plugin registry request failed\");\n\t}\n\treturn new NodeHttpPluginRegistrySourceErrorV1(code, \"Plugin registry request failed\");\n}\n\n/**\n * Creates a bounded, allowlisted HTTP registry reader. It only transports\n * metadata to the existing registry verifier; package bytes, trust decisions,\n * and installation remain separate host-owned capabilities.\n */\nexport function createNodeHttpPluginRegistrySourceV1(\n\toptions: NodeHttpPluginRegistrySourceOptionsV1,\n): NodeHttpPluginRegistrySourceV1 {\n\tconst transport = options.fetch ?? globalThis.fetch;\n\tif (typeof transport !== \"function\") {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_configuration\", \"Node fetch is unavailable\");\n\t}\n\tif (!Array.isArray(options.allowedOrigins) || options.allowedOrigins.length === 0) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\t\"At least one allowed registry origin is required\",\n\t\t);\n\t}\n\tconst origins = new Set(options.allowedOrigins.map(allowedOrigin));\n\tconst endpoint = endpointUrl(options.url, origins);\n\tconst timeoutMs = boundedPositive(\n\t\toptions.requestTimeoutMs,\n\t\tDEFAULT_TIMEOUT_MS_V1,\n\t\tMAX_TIMEOUT_MS_V1,\n\t\t\"requestTimeoutMs\",\n\t);\n\tconst maxResponseBytes = boundedPositive(\n\t\toptions.maxResponseBytes,\n\t\tDEFAULT_MAX_RESPONSE_BYTES_V1,\n\t\tMAX_RESPONSE_BYTES_V1,\n\t\t\"maxResponseBytes\",\n\t);\n\tconst userAgent = options.userAgent ?? \"agent-forge-plugin-registry/1\";\n\tif (userAgent.length === 0 || userAgent.length > 256 || /[\\r\\n]/u.test(userAgent)) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_configuration\", \"userAgent is invalid\");\n\t}\n\tconst source = endpoint.origin + endpoint.pathname;\n\n\treturn {\n\t\tversion: NODE_HTTP_PLUGIN_REGISTRY_SOURCE_VERSION_V1,\n\t\tsource,\n\t\tlist: async ({ signal } = {}) => {\n\t\t\tif (isCallerCancelled(signal)) {\n\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"cancelled\", \"Plugin registry request was cancelled\");\n\t\t\t}\n\t\t\tconst request = requestController(signal, timeoutMs);\n\t\t\tlet response: Response | undefined;\n\t\t\ttry {\n\t\t\t\tconst transportResponse = Promise.resolve().then(() => {\n\t\t\t\t\tif (request.signal.aborted) throw new DOMException(\"aborted\", \"AbortError\");\n\t\t\t\t\treturn transport(endpoint.toString(), {\n\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\taccept: \"application/vnd.agent-forge.plugin-registry+json, application/json\",\n\t\t\t\t\t\t\t\"user-agent\": userAgent,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tredirect: \"error\",\n\t\t\t\t\t\tsignal: request.signal,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t\tvoid transportResponse.then(\n\t\t\t\t\t(lateResponse) => {\n\t\t\t\t\t\tif (request.signal.aborted) discardResponseBody(lateResponse);\n\t\t\t\t\t},\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t\tconst receivedResponse = await awaitBeforeAbort(transportResponse, request.signal);\n\t\t\t\tresponse = receivedResponse;\n\t\t\t\tif (receivedResponse.status < 200 || receivedResponse.status >= 300) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\t\t\t\"request_failed\",\n\t\t\t\t\t\t\"Plugin registry request did not succeed\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!isJsonResponse(receivedResponse)) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\t\t\t\"invalid_response\",\n\t\t\t\t\t\t\"Plugin registry response must be JSON\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst length = responseLength(receivedResponse);\n\t\t\t\tif (length !== undefined && length > maxResponseBytes) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\t\t\t\"response_too_large\",\n\t\t\t\t\t\t\"Plugin registry response exceeds the configured size limit\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn parseDocument(await readBody(receivedResponse, maxResponseBytes, request.signal));\n\t\t\t} catch (error) {\n\t\t\t\tif (isCallerCancelled(signal)) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"cancelled\", \"Plugin registry request was cancelled\");\n\t\t\t\t}\n\t\t\t\tif (request.timedOut()) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"request_timed_out\", \"Plugin registry request timed out\");\n\t\t\t\t}\n\t\t\t\tif (error instanceof NodeHttpPluginRegistrySourceErrorV1) throw sanitizedSourceError(error);\n\t\t\t\tif (isAbortLike(error)) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"cancelled\", \"Plugin registry request was cancelled\");\n\t\t\t\t}\n\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"request_failed\", \"Plugin registry request failed\");\n\t\t\t} finally {\n\t\t\t\trequest.cleanup();\n\t\t\t\tdiscardResponseBody(response);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"node-registry-source.d.ts","sourceRoot":"","sources":["../../../src/plugins/ecosystem/node-registry-source.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAoC,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAE9F,QAAA,MAAM,2CAA2C,GAAa,CAAC;AAM/D,MAAM,MAAM,uCAAuC,GAChD,WAAW,GACX,uBAAuB,GACvB,gBAAgB,GAChB,mBAAmB,GACnB,kBAAkB,GAClB,oBAAoB,CAAC;AAExB;;;GAGG;AACH,qBAAa,mCAAoC,SAAQ,KAAK;IAC7D,QAAQ,CAAC,IAAI,EAAE,uCAAuC,CAAC;IAEvD,YAAY,IAAI,EAAE,uCAAuC,EAAE,OAAO,EAAE,MAAM,EAIzE;CACD;AAED,MAAM,WAAW,qCAAqC;IACrD,wEAAwE;IACxE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IAC3B,gEAAgE;IAChE,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,oDAAoD;IACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACzC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA+B,SAAQ,sBAAsB;IAC7E,QAAQ,CAAC,OAAO,EAAE,OAAO,2CAA2C,CAAC;IACrE,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAwOD;;;;GAIG;AACH,wBAAgB,oCAAoC,CACnD,OAAO,EAAE,qCAAqC,GAC5C,8BAA8B,CAsGhC","sourcesContent":["import { t } from \"../../core/i18n/index.ts\";\nimport type { PluginRegistryManifestEnvelopeV1, PluginRegistrySourceV1 } from \"./registry.ts\";\n\nconst NODE_HTTP_PLUGIN_REGISTRY_SOURCE_VERSION_V1 = 1 as const;\nconst DEFAULT_TIMEOUT_MS_V1 = 15_000;\nconst MAX_TIMEOUT_MS_V1 = 120_000;\nconst DEFAULT_MAX_RESPONSE_BYTES_V1 = 4 * 1024 * 1024;\nconst MAX_RESPONSE_BYTES_V1 = 8 * 1024 * 1024;\n\nexport type NodeHttpPluginRegistrySourceErrorCodeV1 =\n\t| \"cancelled\"\n\t| \"invalid_configuration\"\n\t| \"request_failed\"\n\t| \"request_timed_out\"\n\t| \"invalid_response\"\n\t| \"response_too_large\";\n\n/**\n * Deliberately does not retain transport exceptions or response bodies: callers\n * must not turn registry diagnostics into a credential or path disclosure.\n */\nexport class NodeHttpPluginRegistrySourceErrorV1 extends Error {\n\treadonly code: NodeHttpPluginRegistrySourceErrorCodeV1;\n\n\tconstructor(code: NodeHttpPluginRegistrySourceErrorCodeV1, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"NodeHttpPluginRegistrySourceErrorV1\";\n\t\tthis.code = code;\n\t}\n}\n\nexport interface NodeHttpPluginRegistrySourceOptionsV1 {\n\t/** Absolute endpoint returning the versioned JSON registry document. */\n\treadonly url: string | URL;\n\t/** Exact http(s) origins the host has intentionally allowed. */\n\treadonly allowedOrigins: readonly string[];\n\t/** Real fetch or deterministic test replacement. */\n\treadonly fetch?: typeof globalThis.fetch;\n\treadonly requestTimeoutMs?: number;\n\treadonly maxResponseBytes?: number;\n\treadonly userAgent?: string;\n}\n\nexport interface NodeHttpPluginRegistrySourceV1 extends PluginRegistrySourceV1 {\n\treadonly version: typeof NODE_HTTP_PLUGIN_REGISTRY_SOURCE_VERSION_V1;\n\t/** Sanitized endpoint identity: never contains credentials, query, or fragment. */\n\treadonly source: string;\n}\n\ninterface RegistryDocumentV1 {\n\treadonly version: 1;\n\treadonly entries: readonly PluginRegistryManifestEnvelopeV1[];\n}\n\nfunction boundedPositive(value: number | undefined, fallback: number, maximum: number, label: string): number {\n\tconst result = value ?? fallback;\n\tif (!Number.isSafeInteger(result) || result <= 0 || result > maximum) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\tt(\"{label} must be a positive integer at most {maximum}\", { label, maximum }),\n\t\t);\n\t}\n\treturn result;\n}\n\nfunction allowedOrigin(value: string): string {\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(value);\n\t} catch {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_configuration\", t(\"Allowed registry origin is invalid\"));\n\t}\n\tif ((parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") || parsed.username || parsed.password) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\tt(\"Allowed registry origin must be an http(s) origin without credentials\"),\n\t\t);\n\t}\n\tif (parsed.pathname !== \"/\" || parsed.search !== \"\" || parsed.hash !== \"\") {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\tt(\"Allowed registry origin must not include a path, query, or fragment\"),\n\t\t);\n\t}\n\treturn parsed.origin;\n}\n\nfunction endpointUrl(value: string | URL, origins: ReadonlySet<string>): URL {\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(value);\n\t} catch {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_configuration\", t(\"Plugin registry URL is invalid\"));\n\t}\n\tif ((parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") || parsed.username || parsed.password) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\tt(\"Plugin registry URL must use http(s) without credentials\"),\n\t\t);\n\t}\n\tif (parsed.search !== \"\" || parsed.hash !== \"\") {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\tt(\"Plugin registry URL must not include a query or fragment\"),\n\t\t);\n\t}\n\tif (!origins.has(parsed.origin)) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\tt(\"Plugin registry origin is not allowed\"),\n\t\t);\n\t}\n\treturn parsed;\n}\n\nfunction responseLength(response: Response): number | undefined {\n\tconst raw = response.headers.get(\"content-length\");\n\tif (raw === null) return undefined;\n\tif (!/^\\d+$/u.test(raw)) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_response\",\n\t\t\tt(\"Plugin registry response length is invalid\"),\n\t\t);\n\t}\n\tconst value = Number(raw);\n\tif (!Number.isSafeInteger(value)) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_response\",\n\t\t\tt(\"Plugin registry response length is invalid\"),\n\t\t);\n\t}\n\treturn value;\n}\n\nfunction isJsonResponse(response: Response): boolean {\n\tconst contentType = response.headers.get(\"content-type\");\n\tif (contentType === null) return false;\n\tconst mediaType = contentType.split(\";\", 1)[0]?.trim().toLowerCase();\n\treturn mediaType === \"application/json\" || mediaType === \"application/vnd.agent-forge.plugin-registry+json\";\n}\n\nfunction requestController(\n\tparent: AbortSignal | undefined,\n\ttimeoutMs: number,\n): {\n\treadonly signal: AbortSignal;\n\treadonly timedOut: () => boolean;\n\treadonly cleanup: () => void;\n} {\n\tconst controller = new AbortController();\n\tlet timedOut = false;\n\tconst abort = () => controller.abort(parent?.reason);\n\tif (parent?.aborted === true) abort();\n\telse parent?.addEventListener(\"abort\", abort, { once: true });\n\tconst timeout = setTimeout(() => {\n\t\ttimedOut = true;\n\t\tcontroller.abort();\n\t}, timeoutMs);\n\treturn {\n\t\tsignal: controller.signal,\n\t\ttimedOut: () => timedOut,\n\t\tcleanup: () => {\n\t\t\tclearTimeout(timeout);\n\t\t\tparent?.removeEventListener(\"abort\", abort);\n\t\t},\n\t};\n}\n\nfunction abortPromise(signal: AbortSignal): { readonly promise: Promise<never>; readonly cleanup: () => void } {\n\tlet rejectAbort: (() => void) | undefined;\n\tconst promise = new Promise<never>((_resolve, reject) => {\n\t\trejectAbort = () => reject(new DOMException(\"aborted\", \"AbortError\"));\n\t\tif (signal.aborted) rejectAbort();\n\t\telse signal.addEventListener(\"abort\", rejectAbort, { once: true });\n\t});\n\treturn {\n\t\tpromise,\n\t\tcleanup: () => {\n\t\t\tif (rejectAbort !== undefined) signal.removeEventListener(\"abort\", rejectAbort);\n\t\t},\n\t};\n}\n\nasync function awaitBeforeAbort<T>(operation: Promise<T>, signal: AbortSignal): Promise<T> {\n\tconst abort = abortPromise(signal);\n\ttry {\n\t\treturn await Promise.race([operation, abort.promise]);\n\t} finally {\n\t\tabort.cleanup();\n\t}\n}\n\nfunction discardResponseBody(response: Response | undefined): void {\n\tconst responseBody = response?.body;\n\tif (responseBody !== undefined && responseBody !== null) {\n\t\tvoid responseBody.cancel().catch(() => undefined);\n\t}\n}\n\nasync function readBody(response: Response, maxBytes: number, signal: AbortSignal): Promise<Uint8Array> {\n\tif (response.body === null) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_response\", t(\"Plugin registry response has no body\"));\n\t}\n\tconst reader = response.body.getReader();\n\tconst chunks: Uint8Array[] = [];\n\tlet total = 0;\n\ttry {\n\t\tfor (;;) {\n\t\t\tif (signal.aborted) throw new DOMException(t(\"aborted\"), \"AbortError\");\n\t\t\tconst item = await awaitBeforeAbort(reader.read(), signal);\n\t\t\tif (item.done) break;\n\t\t\ttotal += item.value.byteLength;\n\t\t\tif (total > maxBytes) {\n\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\t\t\"response_too_large\",\n\t\t\t\t\tt(\"Plugin registry response exceeds the configured size limit\"),\n\t\t\t\t);\n\t\t\t}\n\t\t\tchunks.push(item.value);\n\t\t}\n\t} finally {\n\t\treader.releaseLock();\n\t}\n\tconst bytes = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tbytes.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\treturn bytes;\n}\n\nfunction parseDocument(bytes: Uint8Array): readonly PluginRegistryManifestEnvelopeV1[] {\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes));\n\t} catch {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_response\",\n\t\t\tt(\"Plugin registry response is not valid JSON\"),\n\t\t);\n\t}\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_response\", t(\"Plugin registry document is invalid\"));\n\t}\n\tconst document = value as Record<string, unknown>;\n\tif (\n\t\tObject.keys(document).some((key) => key !== \"version\" && key !== \"entries\") ||\n\t\tdocument.version !== 1 ||\n\t\t!Array.isArray(document.entries)\n\t) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_response\", t(\"Plugin registry document is invalid\"));\n\t}\n\treturn document.entries as unknown as RegistryDocumentV1[\"entries\"];\n}\n\nfunction isAbortLike(error: unknown): boolean {\n\treturn error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction isCallerCancelled(signal: AbortSignal | undefined): boolean {\n\treturn signal?.aborted === true;\n}\n\nfunction sanitizedSourceError(error: NodeHttpPluginRegistrySourceErrorV1): NodeHttpPluginRegistrySourceErrorV1 {\n\tconst code: unknown = error.code;\n\tif (\n\t\tcode !== \"cancelled\" &&\n\t\tcode !== \"invalid_configuration\" &&\n\t\tcode !== \"request_failed\" &&\n\t\tcode !== \"request_timed_out\" &&\n\t\tcode !== \"invalid_response\" &&\n\t\tcode !== \"response_too_large\"\n\t) {\n\t\treturn new NodeHttpPluginRegistrySourceErrorV1(\"request_failed\", t(\"Plugin registry request failed\"));\n\t}\n\treturn new NodeHttpPluginRegistrySourceErrorV1(code, t(\"Plugin registry request failed\"));\n}\n\n/**\n * Creates a bounded, allowlisted HTTP registry reader. It only transports\n * metadata to the existing registry verifier; package bytes, trust decisions,\n * and installation remain separate host-owned capabilities.\n */\nexport function createNodeHttpPluginRegistrySourceV1(\n\toptions: NodeHttpPluginRegistrySourceOptionsV1,\n): NodeHttpPluginRegistrySourceV1 {\n\tconst transport = options.fetch ?? globalThis.fetch;\n\tif (typeof transport !== \"function\") {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_configuration\", t(\"Node fetch is unavailable\"));\n\t}\n\tif (!Array.isArray(options.allowedOrigins) || options.allowedOrigins.length === 0) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\"invalid_configuration\",\n\t\t\tt(\"At least one allowed registry origin is required\"),\n\t\t);\n\t}\n\tconst origins = new Set(options.allowedOrigins.map(allowedOrigin));\n\tconst endpoint = endpointUrl(options.url, origins);\n\tconst timeoutMs = boundedPositive(\n\t\toptions.requestTimeoutMs,\n\t\tDEFAULT_TIMEOUT_MS_V1,\n\t\tMAX_TIMEOUT_MS_V1,\n\t\t\"requestTimeoutMs\",\n\t);\n\tconst maxResponseBytes = boundedPositive(\n\t\toptions.maxResponseBytes,\n\t\tDEFAULT_MAX_RESPONSE_BYTES_V1,\n\t\tMAX_RESPONSE_BYTES_V1,\n\t\t\"maxResponseBytes\",\n\t);\n\tconst userAgent = options.userAgent ?? \"agent-forge-plugin-registry/1\";\n\tif (userAgent.length === 0 || userAgent.length > 256 || /[\\r\\n]/u.test(userAgent)) {\n\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"invalid_configuration\", t(\"userAgent is invalid\"));\n\t}\n\tconst source = endpoint.origin + endpoint.pathname;\n\n\treturn {\n\t\tversion: NODE_HTTP_PLUGIN_REGISTRY_SOURCE_VERSION_V1,\n\t\tsource,\n\t\tlist: async ({ signal } = {}) => {\n\t\t\tif (isCallerCancelled(signal)) {\n\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"cancelled\", t(\"Plugin registry request was cancelled\"));\n\t\t\t}\n\t\t\tconst request = requestController(signal, timeoutMs);\n\t\t\tlet response: Response | undefined;\n\t\t\ttry {\n\t\t\t\tconst transportResponse = Promise.resolve().then(() => {\n\t\t\t\t\tif (request.signal.aborted) throw new DOMException(t(\"aborted\"), \"AbortError\");\n\t\t\t\t\treturn transport(endpoint.toString(), {\n\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\taccept: \"application/vnd.agent-forge.plugin-registry+json, application/json\",\n\t\t\t\t\t\t\t\"user-agent\": userAgent,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tredirect: \"error\",\n\t\t\t\t\t\tsignal: request.signal,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t\tvoid transportResponse.then(\n\t\t\t\t\t(lateResponse) => {\n\t\t\t\t\t\tif (request.signal.aborted) discardResponseBody(lateResponse);\n\t\t\t\t\t},\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t\tconst receivedResponse = await awaitBeforeAbort(transportResponse, request.signal);\n\t\t\t\tresponse = receivedResponse;\n\t\t\t\tif (receivedResponse.status < 200 || receivedResponse.status >= 300) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\t\t\t\"request_failed\",\n\t\t\t\t\t\tt(\"Plugin registry request did not succeed\"),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!isJsonResponse(receivedResponse)) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\t\t\t\"invalid_response\",\n\t\t\t\t\t\tt(\"Plugin registry response must be JSON\"),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst length = responseLength(receivedResponse);\n\t\t\t\tif (length !== undefined && length > maxResponseBytes) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\t\t\t\"response_too_large\",\n\t\t\t\t\t\tt(\"Plugin registry response exceeds the configured size limit\"),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn parseDocument(await readBody(receivedResponse, maxResponseBytes, request.signal));\n\t\t\t} catch (error) {\n\t\t\t\tif (isCallerCancelled(signal)) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"cancelled\", t(\"Plugin registry request was cancelled\"));\n\t\t\t\t}\n\t\t\t\tif (request.timedOut()) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\n\t\t\t\t\t\t\"request_timed_out\",\n\t\t\t\t\t\tt(\"Plugin registry request timed out\"),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (error instanceof NodeHttpPluginRegistrySourceErrorV1) throw sanitizedSourceError(error);\n\t\t\t\tif (isAbortLike(error)) {\n\t\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"cancelled\", t(\"Plugin registry request was cancelled\"));\n\t\t\t\t}\n\t\t\t\tthrow new NodeHttpPluginRegistrySourceErrorV1(\"request_failed\", t(\"Plugin registry request failed\"));\n\t\t\t} finally {\n\t\t\t\trequest.cleanup();\n\t\t\t\tdiscardResponseBody(response);\n\t\t\t}\n\t\t},\n\t};\n}\n"]}