@fangorn-network/westmarch 2026.10.27-dev → 2026.10.28

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 (342) hide show
  1. package/dist/ts/eval.js +0 -0
  2. package/package.json +11 -11
  3. package/site/worker.js +27 -8
  4. package/src/agent/agent-card.js +18 -1
  5. package/src/agent/mcp.js +15 -5
  6. package/src/discover/apps.js +15 -1
  7. package/src/publish/ship.js +27 -6
  8. package/src/publish/view.js +134 -40
  9. package/template/package.json +1 -1
  10. package/site/dist/assets/AccountNotFoundScreen-C4njXc69-DyVH4CEz.js +0 -1
  11. package/site/dist/assets/AddFundsSelectionScreen-RAoCn7ej-Dz1--SS9.js +0 -31
  12. package/site/dist/assets/Address-H8kD1d9O-BdFZmRwI.js +0 -17
  13. package/site/dist/assets/AffirmativeConsentScreen-BG0MJxku-CxaCAisL.js +0 -6
  14. package/site/dist/assets/AllowlistRejectionScreen-Cae6e1Od-JGk7vndN.js +0 -1
  15. package/site/dist/assets/ApiController-CTLbMCcH.js +0 -5
  16. package/site/dist/assets/ArrowsRightLeftIcon-B8g9adpz.js +0 -1
  17. package/site/dist/assets/AuthenticateWithWalletScreen-CU6hIcFT-CXD9_OP8.js +0 -1
  18. package/site/dist/assets/AwaitingEvmToSolBridgingScreen-D0e_sZMS-BtJCglvU.js +0 -1
  19. package/site/dist/assets/AwaitingExternalEthereumTransferScreen-ur5HL4R7-BgC2TsqH.js +0 -1
  20. package/site/dist/assets/AwaitingPasswordlessCodeScreen-DRHz76mM-B9OzLeEO.js +0 -103
  21. package/site/dist/assets/AwaitingSolToEvmBridgingScreen-CdLRrGD2-C7r5snUL.js +0 -1
  22. package/site/dist/assets/BridgeNetworkSelectionView-CFGjB2nO-BTV0NRio.js +0 -68
  23. package/site/dist/assets/CaptchaScreen-DbpvkIGx-7rSZIuM-.js +0 -1
  24. package/site/dist/assets/CheckCircleIcon-B1l17ioc.js +0 -1
  25. package/site/dist/assets/CheckCircleIcon-BGSI8nKf.js +0 -1
  26. package/site/dist/assets/Checkbox-D1EDeo41-BUfq0w99.js +0 -52
  27. package/site/dist/assets/ChevronDownIcon-B6uJvPHu.js +0 -1
  28. package/site/dist/assets/Chip-CZKIKt9K-r9Nk__0-.js +0 -26
  29. package/site/dist/assets/CoinbaseOnrampStatusScreen-BbyCJDXL-DWWNHxmR.js +0 -16
  30. package/site/dist/assets/ConnectEmailForm-B-VRE0MN-CRQwS0Xu.js +0 -3
  31. package/site/dist/assets/ConnectLedgerScreen-DWHABGot-CZcCt31O.js +0 -10
  32. package/site/dist/assets/ConnectOnlyLandingScreen-CZYx9Bmp-C5IOf-SQ.js +0 -1
  33. package/site/dist/assets/ConnectOnlyStatusScreen-DCRzjxHx-cwg74F3A.js +0 -1
  34. package/site/dist/assets/ConnectOrCreateScreen-CUUYUO07-D28oeK6b.js +0 -1
  35. package/site/dist/assets/ConnectPhoneForm-Dek5xn4C-EEdkKeTp.js +0 -95
  36. package/site/dist/assets/ConnectWalletView-DqI-PTvX-C2mEzukj.js +0 -74
  37. package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-CH_7r8iV.js +0 -7
  38. package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-Z33kis0B.js +0 -1
  39. package/site/dist/assets/ConnectorUtil-DH4NePk5.js +0 -21
  40. package/site/dist/assets/ConstantsUtil-DmLsRHAl.js +0 -198
  41. package/site/dist/assets/CopyToClipboard-i_OQSBJr-_G88JCVR.js +0 -39
  42. package/site/dist/assets/CopyableText-CQapvaMr-CGGfc_zM.js +0 -28
  43. package/site/dist/assets/CrossAppAuthScreen-CLw7ymZq-C9Okoc0V.js +0 -1
  44. package/site/dist/assets/CustomLandingScreenView-Cm_MBSyX-D2l6hdSl.js +0 -22
  45. package/site/dist/assets/DelegatedActionsConsentScreen-DYF2U4y5-Dda3KKqS.js +0 -1
  46. package/site/dist/assets/DelegatedActionsRevokeScreen-bPp6Uwyh-DG8kJPt1.js +0 -1
  47. package/site/dist/assets/DepositAddressAddressStepView-4vdkb55L-CcOUfn6f.js +0 -187
  48. package/site/dist/assets/DepositAddressScreen-DBLRs7aI-DeituDNh.js +0 -8
  49. package/site/dist/assets/DepositAddressV3Screen-CDqIEMQw-FDg6K14-.js +0 -1
  50. package/site/dist/assets/EmailInputForm-Cqo1mda8-BmbxvPHC.js +0 -124
  51. package/site/dist/assets/EmbeddedWalletConnectingScreen-D4T7DUzs-B05CQ9rx.js +0 -1
  52. package/site/dist/assets/EmbeddedWalletCreatedScreen-D7WhW5ve-Cxd1b9XH.js +0 -1
  53. package/site/dist/assets/EmbeddedWalletKeyExportScreen-CqV4NYQ3-CrJy0ViX.js +0 -23
  54. package/site/dist/assets/EmbeddedWalletOnAccountCreateScreen-BoKb3Q2S-Bakaa3zu.js +0 -1
  55. package/site/dist/assets/EmbeddedWalletPasswordUpdateScreen-D3rMlqS7-j78vKrcj.js +0 -1
  56. package/site/dist/assets/EmbeddedWalletPasswordUpdateSplashScreen-BPFkWJeE-D3dr7W9_.js +0 -1
  57. package/site/dist/assets/EnrollTotp-Dibew4dp-BXbYV8nI.js +0 -66
  58. package/site/dist/assets/EnvelopeIcon-DjPofnRI.js +0 -1
  59. package/site/dist/assets/ErrorBanner-BcpGRt0h-BhH6_V-9.js +0 -21
  60. package/site/dist/assets/ErrorMessage-D8VaAP5m-Bs7uX2a9.js +0 -8
  61. package/site/dist/assets/ErrorScreen-DipdTWzE-BgPRgnOb.js +0 -4
  62. package/site/dist/assets/ErrorScreen-DipdTWzE-CvxXC6pz.js +0 -1
  63. package/site/dist/assets/ExclamationCircleIcon-BE-WGrut.js +0 -1
  64. package/site/dist/assets/ExclamationTriangleIcon-Ca1Xlc_f.js +0 -1
  65. package/site/dist/assets/FarcasterConnectStatusScreen-51tjUxqq-De2JvZdV.js +0 -75
  66. package/site/dist/assets/FarcasterSignerStatusScreen-6x-Uh95G-bLp3hXoO.js +0 -28
  67. package/site/dist/assets/FiatOnrampScreen-BAhYeiTI-Jr6D6JiZ.js +0 -491
  68. package/site/dist/assets/FingerPrintIcon-DOCbS93y.js +0 -1
  69. package/site/dist/assets/FundSolWalletWithExternalSolanaWallet-WtLwYaOl-A_G5kfa1.js +0 -1
  70. package/site/dist/assets/FundWalletMethodHeader-BWNkokCT-LPv35wkD.js +0 -1
  71. package/site/dist/assets/FundWithBankDepositScreen-DajkHxBD-0hfGrfvf.js +0 -58
  72. package/site/dist/assets/FundingEditAmountScreen-CpvpiO4M-Dz0s_NWR.js +0 -1
  73. package/site/dist/assets/FundingMethodSelectionScreen-Db7vhRlE-ZGm-3-Io.js +0 -1
  74. package/site/dist/assets/GlobeAltIcon-BBRl2MMn.js +0 -1
  75. package/site/dist/assets/GooglePay-B53WnudL-BGiVpB8y.js +0 -1
  76. package/site/dist/assets/HCaptchaWrapper-CUi9LJsp-CHdfIeD_.js +0 -4
  77. package/site/dist/assets/InAppBrowserLoginNotPossible-BMR9br1T-Cgoiow3m.js +0 -1
  78. package/site/dist/assets/InfoBanner-Cb3p1z12-_YqQmRiu.js +0 -22
  79. package/site/dist/assets/InjectedWalletIcon-DLcYOGDj-tuIrKQHH.js +0 -1
  80. package/site/dist/assets/InstallWalletScreen-DTA8dwLv-BMU_wa61.js +0 -1
  81. package/site/dist/assets/JsonTree-BHzNC-ic-DcVr0YF0.js +0 -34
  82. package/site/dist/assets/LabelXs-oqZNqbm_-CNprGVQM.js +0 -6
  83. package/site/dist/assets/LandingScreen-CGWUoSUG-fH6rsQen.js +0 -1
  84. package/site/dist/assets/Layouts-BMRfo5hw-e-sepi6F.js +0 -77
  85. package/site/dist/assets/Link-BdDilT2T-BdwlRJch.js +0 -40
  86. package/site/dist/assets/LinkConflictScreen-C3ZEPsgl-BXKN2Qm5.js +0 -77
  87. package/site/dist/assets/LinkEmailScreen-dKo9frwc-Cyn7Q8p1.js +0 -1
  88. package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-CWiqnwLM.js +0 -117
  89. package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-YmDevJzf.js +0 -1
  90. package/site/dist/assets/LinkPhoneScreen-oIUpfj0w-h7shW-Ay.js +0 -1
  91. package/site/dist/assets/Loader-UAZqxsnw-DJEVblm1.js +0 -62
  92. package/site/dist/assets/LoadingSkeleton-BMsgO5PV-D2atjsP5.js +0 -17
  93. package/site/dist/assets/LockClosedIcon-9EFzWK1C.js +0 -1
  94. package/site/dist/assets/LoginFailedScreen-CN0dJV6U-By3Si77a.js +0 -1
  95. package/site/dist/assets/ManualTransferScreen-CHqcuqbs-BAHPY8rs.js +0 -1
  96. package/site/dist/assets/MfaAuthEnrollmentFlowScreen-Dj7gPTRC-CLjffFG1.js +0 -1
  97. package/site/dist/assets/MfaAuthVerifyFlowScreen-DbFpyWMy-BmQg1Y61.js +0 -1
  98. package/site/dist/assets/MfaEnrollmentFlowScreen-DhLnGgnr-DmBtTtlw.js +0 -1
  99. package/site/dist/assets/MfaVerifyFlowScreen-C_XPjMRP-4a683Bmf.js +0 -1
  100. package/site/dist/assets/ModalFooter-BCuEK_nz-C_71qsaz.js +0 -403
  101. package/site/dist/assets/MoonpayStatusScreen-CzTUtokc-DEHHcEf8.js +0 -16
  102. package/site/dist/assets/OAuthStatusScreen-ufbOa-da-BjAb_WsY.js +0 -1
  103. package/site/dist/assets/OpenLink-CUpJ1mOr-BLWFsPOW.js +0 -39
  104. package/site/dist/assets/PasskeySelectSignupOrLogin-xh2ScC_l-2PJTJxX-.js +0 -1
  105. package/site/dist/assets/PasskeyStatusScreen-Bla8MaOk-DCEg761l.js +0 -6
  106. package/site/dist/assets/PasswordRecoveryScreen-awGXZkQk-8nVjNc8k.js +0 -20
  107. package/site/dist/assets/PhArrowCircleDown-D97G-BOA.js +0 -14
  108. package/site/dist/assets/PhArrowClockwise-DVgyFuez.js +0 -14
  109. package/site/dist/assets/PhArrowDown-87b1BizM.js +0 -14
  110. package/site/dist/assets/PhArrowLeft-Da0MfnbK.js +0 -14
  111. package/site/dist/assets/PhArrowRight-AdrIL4DW.js +0 -14
  112. package/site/dist/assets/PhArrowSquareOut-Bk2CMG3I.js +0 -14
  113. package/site/dist/assets/PhArrowUp-4sgyKPGx.js +0 -14
  114. package/site/dist/assets/PhArrowUpRight-BNghD2zq.js +0 -14
  115. package/site/dist/assets/PhArrowsClockwise-BXRn1GEl.js +0 -14
  116. package/site/dist/assets/PhArrowsDownUp-DM09Jo_3.js +0 -14
  117. package/site/dist/assets/PhArrowsLeftRight-MuKBLUoF.js +0 -14
  118. package/site/dist/assets/PhBank-fHbgCqqZ.js +0 -14
  119. package/site/dist/assets/PhBrowser-2-9m-AkM.js +0 -14
  120. package/site/dist/assets/PhCaretDown-BDNabFuL.js +0 -14
  121. package/site/dist/assets/PhCaretLeft-CI460R-A.js +0 -14
  122. package/site/dist/assets/PhCaretRight-D7ZS3Ls8.js +0 -14
  123. package/site/dist/assets/PhCaretUp-C-iIfyvO.js +0 -14
  124. package/site/dist/assets/PhCheck-DxQXn57r.js +0 -14
  125. package/site/dist/assets/PhCircleHalf-MJZeRBGF.js +0 -14
  126. package/site/dist/assets/PhClock-x3MS3Mwi.js +0 -14
  127. package/site/dist/assets/PhCompass-hYn3HhGN.js +0 -14
  128. package/site/dist/assets/PhCopy-CWNYcABI.js +0 -14
  129. package/site/dist/assets/PhCreditCard-Fy4pzwtt.js +0 -14
  130. package/site/dist/assets/PhCurrencyDollar-BgiaenCi.js +0 -14
  131. package/site/dist/assets/PhDesktop-CxrzCSv5.js +0 -14
  132. package/site/dist/assets/PhDeviceMobile-CsyA5Goi.js +0 -14
  133. package/site/dist/assets/PhDotsThree-D-k1BiYg.js +0 -14
  134. package/site/dist/assets/PhEnvelope-D8kzeNox.js +0 -14
  135. package/site/dist/assets/PhFunnelSimple-CfQs5DAW.js +0 -14
  136. package/site/dist/assets/PhGlobe-DvmSc9_A.js +0 -14
  137. package/site/dist/assets/PhIdentificationCard-CfkZm-5K.js +0 -14
  138. package/site/dist/assets/PhImage-Cf0J2ZfJ.js +0 -14
  139. package/site/dist/assets/PhInfo-Bwi51-2X.js +0 -14
  140. package/site/dist/assets/PhLightbulb-C4FZV7Es.js +0 -14
  141. package/site/dist/assets/PhMagnifyingGlass-C9u8jSCJ.js +0 -14
  142. package/site/dist/assets/PhPaperPlaneRight-zxQVvPLX.js +0 -14
  143. package/site/dist/assets/PhPlus-C4UoW-2n.js +0 -14
  144. package/site/dist/assets/PhPower-CFyWDmgD.js +0 -14
  145. package/site/dist/assets/PhPuzzlePiece-BjoNVfST.js +0 -14
  146. package/site/dist/assets/PhQrCode-CP3tFauY.js +0 -14
  147. package/site/dist/assets/PhQuestion-wOWJBd3G.js +0 -14
  148. package/site/dist/assets/PhQuestionMark-CV0k9C0Y.js +0 -14
  149. package/site/dist/assets/PhSealCheck-vIKzREuN.js +0 -14
  150. package/site/dist/assets/PhSignOut-CxELV-i0.js +0 -14
  151. package/site/dist/assets/PhSpinner-CGDmbzUd.js +0 -14
  152. package/site/dist/assets/PhTrash-C9PD0SAt.js +0 -14
  153. package/site/dist/assets/PhUser-0sWNXQeZ.js +0 -14
  154. package/site/dist/assets/PhVault-6JTskUcC.js +0 -14
  155. package/site/dist/assets/PhWarning-CoKs3gz6.js +0 -14
  156. package/site/dist/assets/PhWarningCircle-BXHw1fCt.js +0 -14
  157. package/site/dist/assets/PhX-DX0SFIqC.js +0 -14
  158. package/site/dist/assets/PhoneIcon-CZ47md80.js +0 -1
  159. package/site/dist/assets/PinInput-DbZ0b1i1-BrTXu3Km.js +0 -247
  160. package/site/dist/assets/PrivyPluginContext-DA-yS5vO-CY60yubk.js +0 -1
  161. package/site/dist/assets/QrCode-DbnZwlkH-CcMrWV5D.js +0 -22
  162. package/site/dist/assets/RecoveryOAuthStatusScreen-CsgtTI2x-DgeDnHOK.js +0 -1
  163. package/site/dist/assets/RecoveryPasswordCreateScreen-Be-qJ-LU-CvJJwLhK.js +0 -1
  164. package/site/dist/assets/RecoverySelectionScreen-D1iGtpmi-CADtGtSV.js +0 -7
  165. package/site/dist/assets/SIWXUtil-Cpiv91-M.js +0 -1
  166. package/site/dist/assets/Screen-D30meWsY-vEusJ7KX.js +0 -175
  167. package/site/dist/assets/ScreenHeader-CHmc4-Lu-BvGTLg53.js +0 -31
  168. package/site/dist/assets/ScreenLayout-OwaQQcxH-cfOTmyl0.js +0 -1
  169. package/site/dist/assets/SelectSourceAsset-CWFog6gm-DxYklZYs.js +0 -140
  170. package/site/dist/assets/SetAutomaticRecoveryScreen-BO_OEqAZ-oeqeo05x.js +0 -14
  171. package/site/dist/assets/SetWalletPasswordForm-DwCTVtAh-BxFee9UA.js +0 -11
  172. package/site/dist/assets/ShieldCheckIcon-eFjsosCA.js +0 -1
  173. package/site/dist/assets/SignRequestScreen-BHKv32KR-z8ixfIma.js +0 -26
  174. package/site/dist/assets/SolanaWalletConnector-C7Lqk-Wz-Bc_lvdiF.js +0 -1
  175. package/site/dist/assets/StackedContainer-B2vaEl56-Do5dXB8-.js +0 -24
  176. package/site/dist/assets/StandardSignAndSendTransactionScreen-CfhIXVtd-DudetZAc.js +0 -20
  177. package/site/dist/assets/Subtitle-CV-2yKE4-DV0o1av3.js +0 -13
  178. package/site/dist/assets/TelegramAuthScreen-UL9zLfO8-Bo3sONvf.js +0 -1
  179. package/site/dist/assets/Title-BnzYV3Is-C3qOPnO_.js +0 -7
  180. package/site/dist/assets/TodoList-DnyULl18-BvxvubbF.js +0 -36
  181. package/site/dist/assets/TransactionDetails-e9nesl30-WWflkOIi.js +0 -109
  182. package/site/dist/assets/TransactionErrorView-BDBNqA_f-CotYKcgf.js +0 -178
  183. package/site/dist/assets/TransferFromWalletScreen-Qvn1Yuno-sVFlWqa8.js +0 -31
  184. package/site/dist/assets/TransferOrBridgeLoadingScreen-Dl1I_iIz-CV2-mY19.js +0 -6
  185. package/site/dist/assets/TurnstileWrapper-B7sNddys-oPbuMTQt.js +0 -1
  186. package/site/dist/assets/UpdateEmailScreen-DVOhPV0H-D_mjSpAy.js +0 -1
  187. package/site/dist/assets/UpdatePhoneScreen-CXRswTNZ-6RLG0ahv.js +0 -1
  188. package/site/dist/assets/UserLimitReachedScreen-5we2DHn8-CaVN0bti.js +0 -1
  189. package/site/dist/assets/Value-DTgR824E-Bp6WGZZm.js +0 -27
  190. package/site/dist/assets/WalletCards-DH1rqayz-D3cOTb9x.js +0 -1
  191. package/site/dist/assets/WalletIcon-DOIZhb2l.js +0 -1
  192. package/site/dist/assets/WalletInfoCard-sq_2wgFg-bOqfl5MA.js +0 -29
  193. package/site/dist/assets/WalletInterstitialScreen-Sh7LfsAh-DWaVkMCA.js +0 -1
  194. package/site/dist/assets/WalletLink-DK5LB5Ha-DjUZvqs-.js +0 -20
  195. package/site/dist/assets/WalletOverflowButton-bfCnp-9T-B6P7DQXA.js +0 -1
  196. package/site/dist/assets/WarningBanner-ZZqCEtZK-Bc2YKZb7.js +0 -21
  197. package/site/dist/assets/__vite-browser-external-NciY4Vu1.js +0 -1
  198. package/site/dist/assets/_esm-BZP3cXTv.js +0 -1
  199. package/site/dist/assets/_esm-ZmzBJpEs.js +0 -6
  200. package/site/dist/assets/accounts-D4AXF17G.js +0 -1
  201. package/site/dist/assets/accounts-IkdFx9nq.js +0 -1
  202. package/site/dist/assets/analytics-mkkvFRju-BRBcadka.js +0 -1
  203. package/site/dist/assets/baseSepolia-Bn1j1Ysw.js +0 -1
  204. package/site/dist/assets/basic-Cz1YiCSk.js +0 -1575
  205. package/site/dist/assets/capitalizeFirstLetter-DmLYqXsO-DtOXx-2n.js +0 -1
  206. package/site/dist/assets/ccip-B1J3oTZx.js +0 -7
  207. package/site/dist/assets/ccip-Bd7261zy.js +0 -1
  208. package/site/dist/assets/ccip-D6EHKpjI.js +0 -1
  209. package/site/dist/assets/chain-scefkYIH.js +0 -5
  210. package/site/dist/assets/check-DHnllI8N.js +0 -1
  211. package/site/dist/assets/chevron-down-1y-r4bGO.js +0 -1
  212. package/site/dist/assets/circle-alert-OwxamXUX.js +0 -1
  213. package/site/dist/assets/circle-check-big-3sZGVC10.js +0 -1
  214. package/site/dist/assets/circle-x-DQjNUV9J.js +0 -1
  215. package/site/dist/assets/client-B-BpRtwY.js +0 -1
  216. package/site/dist/assets/client-BfIR1FvH.js +0 -9
  217. package/site/dist/assets/coinbase-DCPC2L_R-YEuIn3jh.js +0 -3
  218. package/site/dist/assets/copy-DMOX_yn7.js +0 -1
  219. package/site/dist/assets/core-GIEmxmAa.js +0 -12
  220. package/site/dist/assets/createLucideIcon-BsERoFTA.js +0 -1
  221. package/site/dist/assets/defineChain-B-SQDLTG.js +0 -1
  222. package/site/dist/assets/dijkstra-CY95afk4.js +0 -1
  223. package/site/dist/assets/dist-CoS6E-3x.js +0 -1
  224. package/site/dist/assets/dist-Cp6IXeJd.js +0 -1
  225. package/site/dist/assets/dist-CrNxmdZj.js +0 -1
  226. package/site/dist/assets/dist-DYGtf4F0.js +0 -16
  227. package/site/dist/assets/embed.worker-Cnl4VGr3.js +0 -1
  228. package/site/dist/assets/errors-BQ3N-RVt-CZPAWCgC.js +0 -1
  229. package/site/dist/assets/esm-C3DslVJx.js +0 -1
  230. package/site/dist/assets/esm-Ci1ogKuK.js +0 -1
  231. package/site/dist/assets/esm-Co94ENUV.js +0 -37
  232. package/site/dist/assets/esm-bb8SANZh.js +0 -1
  233. package/site/dist/assets/esm-n2lWo7kz.js +0 -1
  234. package/site/dist/assets/ethers-zTjbodk5-Cs648CnD.js +0 -1
  235. package/site/dist/assets/eventemitter3-Dzc_0Uq2.js +0 -1
  236. package/site/dist/assets/external-link-BBPG3rG7.js +0 -1
  237. package/site/dist/assets/farcaster-DPlSjvF5-CzBCmgyL.js +0 -1
  238. package/site/dist/assets/features-CuiHbgEJ.js +0 -2
  239. package/site/dist/assets/fingerprint-pattern-CjLtyIxB.js +0 -1
  240. package/site/dist/assets/floating-ui.react-DmaCfayr.js +0 -1
  241. package/site/dist/assets/floating-ui.react-dom-CaoMF8mP.js +0 -1
  242. package/site/dist/assets/formatErc20TokenAmount-BuPk9xcy-BVqOKQif.js +0 -1
  243. package/site/dist/assets/formatter-CVbp4SlJ.js +0 -1
  244. package/site/dist/assets/get-entropy-details-for-user-dHRH5Gpi-BNiOFXbK.js +0 -1
  245. package/site/dist/assets/getChainName-DjpPdUSc-DUyKDrnp.js +0 -1
  246. package/site/dist/assets/getErc20Balance-jXNpFf0N-q4bV8kQq.js +0 -1
  247. package/site/dist/assets/getErc20TokenInfo-BNTrzLMP-DcnTFrXN.js +0 -1
  248. package/site/dist/assets/getFormattedUsdFromLamports-De3U9GlO-Gx4_wxON.js +0 -1
  249. package/site/dist/assets/getUsdcMintAddress-BVgm9GRo-r2UnDeEy.js +0 -1
  250. package/site/dist/assets/guards-BIW-GiMJ-D3tWTQAR.js +0 -1
  251. package/site/dist/assets/hooks.module-BYZGfwDl.js +0 -1
  252. package/site/dist/assets/hourglass-D_GfQdJr.js +0 -1
  253. package/site/dist/assets/id-UqEwL2BI-CTlvRMA5.js +0 -1
  254. package/site/dist/assets/index-BdEw5wyl-kLbhqLbv.js +0 -1
  255. package/site/dist/assets/index-C2h3EneX.js +0 -8
  256. package/site/dist/assets/index-CWARkn2w-DxuckIFn.js +0 -61
  257. package/site/dist/assets/index-DvMddJcd-CNBb4VUn.js +0 -12
  258. package/site/dist/assets/index-YiCw0Y_x-Czl0tWfF.js +0 -436
  259. package/site/dist/assets/index.browser-B-i-Lj3J.js +0 -1
  260. package/site/dist/assets/index.browser-BMmUcykv.js +0 -1
  261. package/site/dist/assets/index.es-Crd0TyUS.js +0 -2
  262. package/site/dist/assets/index.es-DQbup1WG.js +0 -3
  263. package/site/dist/assets/isPaymentRequestAvailable-Bq1cemEn-Cjf3l05I.js +0 -1
  264. package/site/dist/assets/jsx-runtime-CndCZlyt.js +0 -1
  265. package/site/dist/assets/landmark-BZPqMFEf.js +0 -1
  266. package/site/dist/assets/lib-DGtzi2DK.js +0 -1
  267. package/site/dist/assets/localBatchGatewayRequest-B71J8sQ9.js +0 -29
  268. package/site/dist/assets/lock-DyN_c-ud.js +0 -1
  269. package/site/dist/assets/logger-CsXm3oIv-B0UYpNFI.js +0 -1
  270. package/site/dist/assets/mail-V_mcPb64.js +0 -1
  271. package/site/dist/assets/modal-context-CwmSeFcv-DPWNJrfK.js +0 -1
  272. package/site/dist/assets/native-BKj27wfc.js +0 -1
  273. package/site/dist/assets/ofetch.CWycOUEr-Duby3iHT.js +0 -1
  274. package/site/dist/assets/parseAbi-Csg-8t59.js +0 -2
  275. package/site/dist/assets/parseAbiParameters-BWbIMYe4.js +0 -1
  276. package/site/dist/assets/parseEther-CHS0GNkM.js +0 -1
  277. package/site/dist/assets/pkce-DXF0yKjP-7_VY-d83.js +0 -1
  278. package/site/dist/assets/preload-helper-CRzLMJPS.js +0 -1
  279. package/site/dist/assets/privy-context-81KmsBw2-DiR6-Yq8.js +0 -2
  280. package/site/dist/assets/property-DuZ9DLP4.js +0 -3
  281. package/site/dist/assets/react-BfYA1q0G.js +0 -1
  282. package/site/dist/assets/react-CK2HRI1t.js +0 -1
  283. package/site/dist/assets/react-DxGeY8B8.js +0 -1
  284. package/site/dist/assets/react-auth-sHnipzbS.js +0 -1
  285. package/site/dist/assets/react-dom-BI2Koe4o.js +0 -1
  286. package/site/dist/assets/reservoir-x-nGuZkT-BguVFH3D.js +0 -1
  287. package/site/dist/assets/roles-BDPDWrwo.js +0 -1
  288. package/site/dist/assets/roles-DKPW79Ca.js +0 -1
  289. package/site/dist/assets/rolldown-runtime-Bjp52BOI.js +0 -1
  290. package/site/dist/assets/safe-url-D7SRPu33-CkMedGro.js +0 -1
  291. package/site/dist/assets/secp256k1-BeYL8aZl.js +0 -1
  292. package/site/dist/assets/secp256k1-Bh4MSkhz.js +0 -1
  293. package/site/dist/assets/secp256k1-Ci4cqHl0.js +0 -1
  294. package/site/dist/assets/secp256k1-DsxXqbXv.js +0 -1
  295. package/site/dist/assets/secp256k1-N9RUmafO.js +0 -1
  296. package/site/dist/assets/serializeAuthorizationList-DGMik9Wx.js +0 -1
  297. package/site/dist/assets/serializeSignature-CWKpxoab.js +0 -2
  298. package/site/dist/assets/sha2-C6s7zZmb.js +0 -1
  299. package/site/dist/assets/shared-DRZUmnL5-BewE5wK8.js +0 -193
  300. package/site/dist/assets/shared-FM0rljBt-CxLACri_.js +0 -21
  301. package/site/dist/assets/shouldProceedtoEmbeddedWalletCreationFlow-B3vyavrh-BkAKsRNi.js +0 -1
  302. package/site/dist/assets/simulateTransaction-CfuOrae4-PjA_tGjX.js +0 -1
  303. package/site/dist/assets/smartphone-3p8nFSTE.js +0 -1
  304. package/site/dist/assets/src-BBjkfi-Q.js +0 -1
  305. package/site/dist/assets/storage-Ufl800Ne-1qCR2JMO.js +0 -8
  306. package/site/dist/assets/stringify-DqUj9VzO.js +0 -20
  307. package/site/dist/assets/stripe.esm-DEi2D291.js +0 -2
  308. package/site/dist/assets/styles-BsotlekN-Aq27nZGc.js +0 -9
  309. package/site/dist/assets/styles-Cm9H63WO-p_ic_dxE.js +0 -92
  310. package/site/dist/assets/styles-DVyDvTdj-CjsWqAld.js +0 -74
  311. package/site/dist/assets/styles-hslXy6xG-D5QHIrJU.js +0 -164
  312. package/site/dist/assets/telegram-B-JqnkqZ-Dqkm4rDn.js +0 -1
  313. package/site/dist/assets/to-ui-error-DWe-qwS3-Bi1HUQI9.js +0 -4
  314. package/site/dist/assets/toViemAccount-B5iDGq1b-CdFZwv07.js +0 -2
  315. package/site/dist/assets/transaction-BNTP-bFm-M5tJx8vY.js +0 -1
  316. package/site/dist/assets/transfer-6YztDh-t-BVQkTvG-.js +0 -1
  317. package/site/dist/assets/transformers.web-BTyOEJ0C.js +0 -40
  318. package/site/dist/assets/transformers.web-CUZUFjAK.js +0 -49
  319. package/site/dist/assets/triangle-alert-BG3Qw-Hq.js +0 -1
  320. package/site/dist/assets/twitch-5IOe4sIQ-CaXHFlAu.js +0 -1
  321. package/site/dist/assets/types-4CuOD23i.js +0 -1
  322. package/site/dist/assets/use-headless-crypto-deposit-Yn-QRLB3-CQqeyhGY.js +0 -1
  323. package/site/dist/assets/use-unlink-wallet-BKK4_5Ur-C0xQN_Lp.js +0 -1
  324. package/site/dist/assets/useActiveWallet-B-yZYGc6-BkK6rFBm.js +0 -123
  325. package/site/dist/assets/useGetSolPrice-x7gfUIHJ-CLVXqv6I.js +0 -1
  326. package/site/dist/assets/useGetTokenPrice-CaoUkb79-DAXXs3rs.js +0 -1
  327. package/site/dist/assets/useI18n-C-5n_noF-CGpkgIVy.js +0 -1
  328. package/site/dist/assets/useWalletBalance-CZnP5f_f-Cfd64m4n.js +0 -1
  329. package/site/dist/assets/useWallets-X1ndvJ_U-CAYqg7Za.js +0 -1
  330. package/site/dist/assets/utils-BkR7EQjg.js +0 -1
  331. package/site/dist/assets/utils-DJhBNOZA.js +0 -1
  332. package/site/dist/assets/utils-DdckVfQr.js +0 -1
  333. package/site/dist/assets/w3m-modal-B4H_uiPH.js +0 -1174
  334. package/site/dist/assets/wallet-BNI12IYk.js +0 -1
  335. package/site/dist/assets/wallet-CITSaPlB.js +0 -2
  336. package/site/dist/assets/wallet-connect-BvJtUsDt-CVlSNIXP.js +0 -1
  337. package/site/dist/assets/walletconnect-registry-xxG8XLCb-BpzpBq5s.js +0 -1
  338. package/site/dist/assets/walletconnect-v2-3frQFwbg-CzZ6oybX.js +0 -12
  339. package/site/dist/assets/wui-ux-by-reown-XyHdtV1O.js +0 -1147
  340. package/site/dist/assets/x-gjDrY2rk.js +0 -1
  341. package/site/dist/index.html +0 -21
  342. package/site/dist/theme.css +0 -7
package/dist/ts/eval.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fangorn-network/westmarch",
3
- "version": "2026.10.27-dev",
3
+ "version": "2026.10.28",
4
4
  "description": "all-in-one builder for fangorn apps",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,7 +45,7 @@
45
45
  "@fangorn-network/markov": "^0.1.0"
46
46
  },
47
47
  "peerDependencies": {
48
- "@fangorn-network/sdk": "^2026.10.26-dev",
48
+ "@fangorn-network/sdk": "^2026.10.27-dev",
49
49
  "@huggingface/transformers": "^4.2.0",
50
50
  "@noble/curves": "^1.9.1",
51
51
  "viem": "^2.55.1"
@@ -56,7 +56,7 @@
56
56
  }
57
57
  },
58
58
  "devDependencies": {
59
- "@fangorn-network/sdk": "^2026.10.26-dev",
59
+ "@fangorn-network/sdk": "^2026.10.27-dev",
60
60
  "@huggingface/transformers": "^4.2.0",
61
61
  "@noble/curves": "^1.9.1",
62
62
  "@privy-io/react-auth": "^3.47.0",
@@ -68,14 +68,14 @@
68
68
  "viem": "^2.56.3"
69
69
  },
70
70
  "bin": {
71
- "westmarch": "./src/publish/init.js",
72
- "westmarch-publish": "./src/publish/cli.js",
73
- "westmarch-view": "./src/publish/view-cli.js",
74
- "westmarch-ship": "./src/publish/ship.js",
75
- "westmarch-eval": "./dist/ts/eval.js",
76
- "westmarch-lint": "./src/publish/lint.js",
77
- "fangorn-mcp": "./src/agent/mcp.js",
78
- "fangorn-fx": "./plugin/scripts/fx.mjs"
71
+ "westmarch": "src/publish/init.js",
72
+ "westmarch-publish": "src/publish/cli.js",
73
+ "westmarch-view": "src/publish/view-cli.js",
74
+ "westmarch-ship": "src/publish/ship.js",
75
+ "westmarch-eval": "dist/ts/eval.js",
76
+ "westmarch-lint": "src/publish/lint.js",
77
+ "fangorn-mcp": "src/agent/mcp.js",
78
+ "fangorn-fx": "plugin/scripts/fx.mjs"
79
79
  },
80
80
  "files": [
81
81
  "src",
package/site/worker.js CHANGED
@@ -35,7 +35,7 @@ import { createPublicClient, http } from "viem";
35
35
  import { generatePrivateKey } from "viem/accounts";
36
36
  import { EMBED_MODEL, DOCUMENT, QUERY, matryoshka } from "../src/core/embed.js";
37
37
  import { configure, resetShard } from "../src/core/shard.js";
38
- import { publishView, r2Store } from "../src/publish/view.js";
38
+ import { ORIGIN, publishView, r2Store } from "../src/publish/view.js";
39
39
  import { viewTools } from "../src/agent/view-tools.js";
40
40
 
41
41
  let CONFIG = { paid: null };
@@ -57,8 +57,18 @@ const encode = (env, prompt) => async (texts) =>
57
57
  // ── the indexer ───────────────────────────────────────────────────────────────
58
58
 
59
59
  const STATE = "state.json", LOCK = "index.lock";
60
- // A run past this is presumed dead and its lock taken over. Cron runs get 15 minutes.
61
- const LOCK_MS = 14 * 60_000;
60
+ // A live run beats its lock every BEAT_MS; one silent past LOCK_MS is presumed dead (killed for CPU
61
+ // or wall time, which skips `finally`) and its lock taken over. Lock: "<last beat ms> <run id>".
62
+ // ponytail: get-then-put, so two runs starting in the same instant can both take it; R2's onlyIf etag when that shows.
63
+ const LOCK_MS = 2 * 60_000, BEAT_MS = 30_000;
64
+ export async function takeLock(bucket, now = Date.now()) {
65
+ const held = await (await bucket.get(LOCK))?.text();
66
+ if (held && now - Number(held.split(" ")[0]) < LOCK_MS) return null;
67
+ const id = crypto.randomUUID();
68
+ const beat = () => bucket.put(LOCK, `${Date.now()} ${id}`);
69
+ await beat();
70
+ return { id, beat, release: async () => { if ((await (await bucket.get(LOCK))?.text())?.endsWith(id)) await bucket.delete(LOCK); } };
71
+ }
62
72
 
63
73
  // CONFIG.network: the SDK's network name ("baseSepolia"); absent, its default.
64
74
  const network = () => SupportedNetworks[CONFIG.network] ?? FangornConfig;
@@ -96,9 +106,9 @@ export async function presented({ fangorn, chain }, state) {
96
106
  }
97
107
 
98
108
  export async function index(env, { log = console.log } = {}) {
99
- const lock = await env.VIEW.get(LOCK);
100
- if (lock && Date.now() - Number(await lock.text()) < LOCK_MS) return log("index: a run is in progress; skipped");
101
- await env.VIEW.put(LOCK, String(Date.now()));
109
+ const lock = await takeLock(env.VIEW);
110
+ if (!lock) return log("index: a run is in progress; skipped");
111
+ const beating = setInterval(() => lock.beat().catch(() => {}), BEAT_MS);
102
112
  try {
103
113
  const state = (await (await env.VIEW.get(STATE))?.json()) ?? { block: null, repos: {} };
104
114
  const r = reader(env);
@@ -113,7 +123,8 @@ export async function index(env, { log = console.log } = {}) {
113
123
  log(`index: through block ${state.block}, ${Object.keys(report).length} domain(s) changed`);
114
124
  return report;
115
125
  } finally {
116
- await env.VIEW.delete(LOCK);
126
+ clearInterval(beating);
127
+ await lock.release();
117
128
  }
118
129
  }
119
130
 
@@ -455,7 +466,6 @@ async function paidCall(origin, tool, payment, run) {
455
466
  return { ...result, _meta: { ...result._meta, "x402/payment-response": settled } };
456
467
  }
457
468
 
458
- export const ORIGIN = "https://origin.westmarch.invalid";
459
469
  const BY_ORIGIN = new Set(["/.well-known/agent-card.json", "/openapi.json", "/.well-known/x402"]);
460
470
 
461
471
  const worker = {
@@ -517,6 +527,15 @@ if (typeof process !== "undefined" && import.meta.url === `file://${process.argv
517
527
  delimitedPrefixes: [...new Set(ks.filter((k) => rest(k).includes("/")).map((k) => `${prefix + rest(k).split("/")[0]}/`))] };
518
528
  },
519
529
  };
530
+ const a = await takeLock(VIEW);
531
+ eq([!!a, await takeLock(VIEW)], [true, null], "a held lock skips the next run");
532
+ objs.set(LOCK, { bytes: new TextEncoder().encode(`${Date.now() - LOCK_MS - 1} dead`), at: new Date() });
533
+ const b = await takeLock(VIEW);
534
+ await a.release();
535
+ eq([!!b, objs.has(LOCK)], [true, true], "a silent lock is taken over, and its dead owner can't release the new one");
536
+ await b.release();
537
+ eq(objs.has(LOCK), false, "the owner releases its own lock");
538
+
520
539
  // Words → a 768-d vector, so a query shares dimensions with the rows that share its words.
521
540
  const calls = [];
522
541
  const AI = { run: async (model, { text }) => { calls.push({ model, text });
@@ -17,6 +17,7 @@
17
17
  // point back at it, so writing someone else's appId here gets you nothing.
18
18
 
19
19
  import { FANGORN_APP_EXTENSION, BaseSepoliaConfig, appId as hashAppId } from "@fangorn-network/sdk";
20
+ import { PLUGIN_MARKETPLACE, PLUGIN_NAME } from "../discover/apps.js";
20
21
 
21
22
  /** Run `register` against a recording `document.modelContext`; return what it
22
23
  * registered. `register` is whatever the page calls at load. */
@@ -48,6 +49,10 @@ const HEX32 = /^0x[0-9a-fA-F]{64}$/;
48
49
  * tools, as the free tier's Worker does at /mcp.
49
50
  * config the deployment; defaults to the SDK's, so the addresses in the
50
51
  * card cannot drift from the ones `discoverApp` checks.
52
+ * skill `{url, sha256}`: the app's own agent skill (a SKILL.md), for agents
53
+ * that adopt it, so its behaviour travels with the app and not with a
54
+ * marketplace. The hash pins what the reader saw: an agent adopts it
55
+ * only on its user's yes, and asks again when the hash changes.
51
56
  */
52
57
  export function agentCard({ name, description, url, version = "0.0.0", tools = [], tags = [], skills = [], fangorn, ...rest }) {
53
58
  if (!name || !url) throw new Error("agentCard: name and url are required");
@@ -75,7 +80,7 @@ export function agentCard({ name, description, url, version = "0.0.0", tools = [
75
80
 
76
81
  /** The params `discoverApp` reads, validated the same way it validates them.
77
82
  * A malformed card should fail the build, not the first reader. */
78
- export function fangornExtension({ app, fromBlock, repos = [], views = [], mcp, paid, access, config = BaseSepoliaConfig }) {
83
+ export function fangornExtension({ app, fromBlock, repos = [], views = [], mcp, paid, access, skill, plugin, config = BaseSepoliaConfig }) {
79
84
  if (!app) throw new Error("fangorn.app is required");
80
85
  const id = HEX32.test(app) ? app : hashAppId(app);
81
86
  const from = String(fromBlock ?? "");
@@ -96,6 +101,10 @@ export function fangornExtension({ app, fromBlock, repos = [], views = [], mcp,
96
101
  }
97
102
  // `access`: a private app. Its view and tools need a pass (`pass_url`, `pass` in base units, for
98
103
  // `days`) or one x402 payment of `call` per tool call; describe and the catalog are free.
104
+ if (skill && !(/^https?:\/\//.test(skill.url ?? "") && /^[0-9a-f]{64}$/.test(skill.sha256 ?? "")))
105
+ throw new Error(`fangorn.skill needs an http(s) url and the sha256 of the file, in hex: ${JSON.stringify(skill)}`);
106
+ if (plugin && !(PLUGIN_MARKETPLACE.test(plugin.marketplace ?? "") && PLUGIN_NAME.test(plugin.name ?? "")))
107
+ throw new Error(`fangorn.plugin needs a marketplace (GitHub owner/repo or https URL) and a plugin name: ${JSON.stringify(plugin)}`);
99
108
  if (access && !(/^https?:\/\//.test(access.pass_url ?? "") && /^\d+$/.test(String(access.pass)) && /^\d+$/.test(String(access.call))))
100
109
  throw new Error(`fangorn.access needs pass_url, and pass and call in base units: ${JSON.stringify(access)}`);
101
110
  return {
@@ -110,6 +119,8 @@ export function fangornExtension({ app, fromBlock, repos = [], views = [], mcp,
110
119
  ...(paid ? { paid: { url: paid.url, price: String(paid.price), asset: paid.asset, network: paid.network,
111
120
  symbol: paid.symbol ?? "USDC", decimals: paid.decimals ?? 6, description: paid.description ?? "" } } : {}),
112
121
  ...(access ? { access } : {}),
122
+ ...(skill ? { skill: { url: skill.url, sha256: skill.sha256 } } : {}),
123
+ ...(plugin ? { plugin: { marketplace: plugin.marketplace, name: plugin.name } } : {}),
113
124
  },
114
125
  };
115
126
  }
@@ -148,6 +159,12 @@ if (typeof process !== "undefined" && import.meta.url === `file://${process.argv
148
159
  throws(() => fangornExtension({ app: "a" }), /fromBlock/, "missing fromBlock");
149
160
  throws(() => fangornExtension({ app: "a", fromBlock: 0, views: ["javascript:alert(1)"] }), /views/, "non-http view");
150
161
  throws(() => agentCard({ name: "X" }), /url/, "missing url");
162
+ const sha = "a".repeat(64);
163
+ eq(fangornExtension({ app: "a", fromBlock: 0, skill: { url: "https://x.test/skill/a/SKILL.md", sha256: sha } }).params.skill, { url: "https://x.test/skill/a/SKILL.md", sha256: sha }, "a skill rides in the extension");
164
+ throws(() => fangornExtension({ app: "a", fromBlock: 0, skill: { url: "https://x.test/s.md" } }), /skill/, "a skill without its hash");
165
+ throws(() => fangornExtension({ app: "a", fromBlock: 0, skill: { url: "file:///etc/passwd", sha256: sha } }), /skill/, "a non-http skill");
166
+ eq(fangornExtension({ app: "a", fromBlock: 0, plugin: { marketplace: "o/r", name: "a" } }).params.plugin, { marketplace: "o/r", name: "a" }, "a plugin rides in the extension");
167
+ throws(() => fangornExtension({ app: "a", fromBlock: 0, plugin: { marketplace: "o/r; rm -rf ~", name: "a" } }), /plugin/, "a plugin that would inject into a shell");
151
168
 
152
169
  console.log("agent-card.js self-check ok — tools captured without executing, extension matches the SDK's, bad params fail the build");
153
170
  }
package/src/agent/mcp.js CHANGED
@@ -24,7 +24,7 @@ import net from "node:net";
24
24
  import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
25
25
  import { homedir } from "node:os";
26
26
  import { createInterface } from "node:readline";
27
- import { APP_EXTENSION, listApps, toApp } from "../discover/apps.js";
27
+ import { APP_EXTENSION, listApps, pluginOf, skillOf, toApp } from "../discover/apps.js";
28
28
 
29
29
  const log = (...a) => console.error("[fangorn-mcp]", ...a); // stdout is the protocol
30
30
  // FANGORN_MCP_TRACE=1: step timings on stderr, for finding where an answer waits.
@@ -214,8 +214,9 @@ export function handlers(deps) {
214
214
  if (!mcp) throw new Error(`${card.name ?? cardUrl} has no MCP server (its card has no \`mcp\`)`);
215
215
  let s = slug(card.name ?? app);
216
216
  while (open.has(s)) s += "-";
217
- const access = card.capabilities?.extensions?.find((e) => e.uri === APP_EXTENSION)?.params?.access ?? null;
218
- const o = { slug: s, name: card.name, card: cardUrl, appId, ...await deps.remoteTools(mcp, { access }) };
217
+ const ext = card.capabilities?.extensions?.find((e) => e.uri === APP_EXTENSION);
218
+ const access = ext?.params?.access ?? null;
219
+ const o = { slug: s, name: card.name, card: cardUrl, appId, skill: skillOf(ext), plugin: pluginOf(ext), ...await deps.remoteTools(mcp, { access }) };
219
220
  trace(`open ${app}: ready`);
220
221
  open.set(o.slug, o);
221
222
  deps.notify?.("notifications/tools/list_changed");
@@ -249,7 +250,13 @@ export function handlers(deps) {
249
250
  "open-app": async ({ app }) => {
250
251
  const o = await openApp(app);
251
252
  return text({ app: o.slug, name: o.name, card: o.card,
252
- call_as: `${o.slug}__<tool>, or call-app-tool`, tools: o.tools });
253
+ call_as: `${o.slug}__<tool>, or call-app-tool`, tools: o.tools,
254
+ // An app's own skill: standing instructions for the reader's agent, so never loaded
255
+ // silently. The fangorn-index skill offers it to the user and pins the hash on a yes.
256
+ ...(o.skill ? { skill: { ...o.skill, adopt: "The app ships an agent skill. Offer it to the user (what it would make you do, from its description); adopt it only on their yes, checking sha256, and ask again if the hash changes." } } : {}),
257
+ // Its Claude Code plugin: hooks run code on every matching event, so the same rule, louder.
258
+ ...(o.plugin ? { plugin: { ...o.plugin, install: `claude plugin install ${o.plugin.name} --marketplace ${o.plugin.marketplace}`,
259
+ adopt: "The app ships a Claude Code plugin, whose hooks run code on this machine without being called. Offer it to the user once, saying what its hooks trigger on; install only on their yes. It takes effect next session." } } : {}) });
253
260
  },
254
261
  "call-app-tool": async ({ app, tool, arguments: args }) => call(app, tool, args),
255
262
  };
@@ -298,7 +305,8 @@ if (process.argv[2] === "--selfcheck") {
298
305
  findApps: async () => ({ apps: [{ name: "Kings Foil", desc: "trials", card: "https://k.test/card", appId: "0xk" },
299
306
  { name: "Bare", desc: "", card: "https://b.test/card", appId: "0xb" }], rejected: [{}] }),
300
307
  exploreApps: async (q) => ({ ranked_by: q ? "meaning" : null, apps: [{ app: "kings-foil", name: "Kings Foil", relevant: true }, { app: "b", name: "B", relevant: false }] }),
301
- resolveCard: async (url) => (url.includes("b.test") ? { card: { name: "Bare" }, mcp: null } : { card: { name: "Kings Foil" }, mcp: "https://k.test/mcp" }),
308
+ resolveCard: async (url) => (url.includes("b.test") ? { card: { name: "Bare" }, mcp: null } : { card: { name: "Kings Foil", capabilities: { extensions: [{ uri: APP_EXTENSION,
309
+ params: { skill: { url: "https://k.test/skill/kings-foil/SKILL.md", sha256: "b".repeat(64) }, plugin: { marketplace: "k/kf", name: "kings-foil" } } }] } }, mcp: "https://k.test/mcp" }),
302
310
  remoteTools: async () => (opened++, remote),
303
311
  };
304
312
  deps.notify = serve(handlers(deps), { input, output });
@@ -321,6 +329,8 @@ if (process.argv[2] === "--selfcheck") {
321
329
  call(5, "open-app", { app: "Kings Foil" }); await wait();
322
330
  call(6, "open-app", { app: "https://k.test/card" }); await wait();
323
331
  assert(body(5).tools[0].name === "search" && opened === 1, "open once, by name or by card URL");
332
+ assert(body(5).skill?.url === "https://k.test/skill/kings-foil/SKILL.md" && /user/.test(body(5).skill.adopt), "open-app offers the app's skill, for the user to adopt");
333
+ assert(body(5).plugin?.marketplace === "k/kf" && /user/.test(body(5).plugin.adopt), "open-app offers the app's plugin, for the user to install");
324
334
  assert(replies.some((r) => r.method === "notifications/tools/list_changed"), "opening notifies");
325
335
  rpc(7, "tools/list"); call(8, "kings-foil__search", { query: "x" }); call(9, "call-app-tool", { app: "kings-foil", tool: "search" });
326
336
  call(10, "call-app-tool", { app: "kings-foil", tool: "boom" }); call(11, "open-app", { app: "nope" }); call(13, "open-app", { app: "Bare" }); rpc(12, "nope"); await wait();
@@ -33,6 +33,20 @@ function safeView(url) {
33
33
  * src/agent/agent-card.js's self-check fails if the two drift. */
34
34
  export const APP_EXTENSION = "https://fangorn.network/a2a/app/v1";
35
35
 
36
+ /** The app's agent skill, `{url, sha256}`, when its card names one well-formed; else null. */
37
+ export const skillOf = (ext) => {
38
+ const k = ext?.params?.skill;
39
+ return k && safeView(k.url) && /^[0-9a-f]{64}$/.test(k.sha256 ?? "") ? { url: k.url, sha256: k.sha256 } : null;
40
+ };
41
+
42
+ // The app's Claude Code plugin, for what a skill can't do (hooks). Both fields reach a shell command
43
+ // in the reader's agent, so they are held to a GitHub owner/repo (or https URL) and a plain name.
44
+ export const PLUGIN_MARKETPLACE = /^([\w.-]+\/[\w.-]+|https:\/\/[\w.\/-]+)$/, PLUGIN_NAME = /^[a-z0-9][a-z0-9-]*$/;
45
+ export const pluginOf = (ext) => {
46
+ const p = ext?.params?.plugin;
47
+ return p && PLUGIN_MARKETPLACE.test(p.marketplace ?? "") && PLUGIN_NAME.test(p.name ?? "") ? { marketplace: p.marketplace, name: p.name } : null;
48
+ };
49
+
36
50
  /** A verified card → an app entry. `views` is westmarch's param; the SDK does not read it. */
37
51
  export function toApp({ card, appId, fromBlock, repos }, cardUrl) {
38
52
  const ext = card.capabilities.extensions.find((e) => e.uri === APP_EXTENSION);
@@ -40,7 +54,7 @@ export function toApp({ card, appId, fromBlock, repos }, cardUrl) {
40
54
  const p = ext?.params?.paid;
41
55
  const paid = p && /^https?:\/\//.test(p.url ?? "") && p.url.includes("{id}") && /^\d+$/.test(String(p.price)) ? p : null;
42
56
  return {
43
- appId, card: cardUrl, fromBlock, repos, views, paid,
57
+ appId, card: cardUrl, fromBlock, repos, views, paid, skill: skillOf(ext), plugin: pluginOf(ext),
44
58
  mcp: safeView(ext?.params?.mcp),
45
59
  name: card.name ?? appId,
46
60
  desc: card.description ?? "",
@@ -83,6 +83,9 @@ const log = (s) => console.error(`[ship] ${s}`);
83
83
  // // (pages/intro.html, a fragment, replaces the description on the front page)
84
84
  // "nav": [{ "href": "coverage.html", "label": "Coverage" }], // optional: header links to them
85
85
  // "examples": ["…"], // optional: questions to start from, on the front page (and a private app's gate)
86
+ // "plugin": {"marketplace": "owner/repo", "name": "…"}, // optional: its Claude Code plugin (hooks), offered by open-app
87
+ // "skill": "skill/SKILL.md", // optional: the app's agent skill, served at skill/<app>/SKILL.md and named
88
+ // // in the card with its sha256; fangorn-index offers it to whoever opens the app
86
89
  // "agent": "app/agent.js" }, // optional: the module whose registerAgent the card lists, when
87
90
  // // `pages` replaces the stock index.html with a page of its own
88
91
  // "types": { "<tag>": { "description": "…", "role_map": {…}, "presentation": {…} } },
@@ -163,10 +166,13 @@ export function presentedRepos(cfg) {
163
166
 
164
167
  /** The Worker's wrangler config: the stock worker, the site as its assets, its bucket, the AI
165
168
  * binding, the cron and CONFIG. `worker` is site/worker.js unless a test points elsewhere. */
166
- export function wranglerConfig({ name, site, account, cron = "*/5 * * * *", config, url, ipfsGateway, worker = join(HERE, "../../site/worker.js") }) {
169
+ export function wranglerConfig({ name, site, account, cron = "*/5 * * * *", cpuMs, config, url, ipfsGateway, worker = join(HERE, "../../site/worker.js") }) {
167
170
  return {
168
171
  name, main: worker, compatibility_date: "2025-09-15", compatibility_flags: ["nodejs_compat"],
169
172
  ...(account ? { account_id: account } : {}),
173
+ // site.cpu_ms: the indexer's CPU per run. Reading one big commit (50k records) outlasts the
174
+ // 30 s Workers Paid default; up to 300000. The Free plan refuses the deploy with it set.
175
+ ...(cpuMs ? { limits: { cpu_ms: cpuMs } } : {}),
170
176
  assets: { directory: site, binding: "ASSETS" },
171
177
  r2_buckets: [{ binding: "VIEW", bucket_name: `${name}-view` }],
172
178
  ai: { binding: "AI" },
@@ -270,7 +276,7 @@ export async function buildForDeploy(configPath, wranglerPath = join(dirname(res
270
276
  log(`the R2 bucket ${w.r2_buckets?.[0]?.bucket_name} doesn't name the Worker; two apps sharing one overwrite each other's view`);
271
277
 
272
278
  process.env.FANGORN_NETWORK = process.env.CHAIN_NAME = cfg.network;
273
- const [{ createPublicClient, http }, view, { ORIGIN }] = await Promise.all([import("viem"), import("./view.js"), import("../../site/worker.js")]);
279
+ const [{ createPublicClient, http }, view] = await Promise.all([import("viem"), import("./view.js")]);
274
280
  const FangornConfig = await view.network(cfg.network);
275
281
  const fangorn = await view.readOnlyFangorn();
276
282
  fangorn.setAppId(cfg.app);
@@ -281,7 +287,7 @@ export async function buildForDeploy(configPath, wranglerPath = join(dirname(res
281
287
 
282
288
  const base = dirname(resolve(configPath));
283
289
  const { site, writeCard } = await buildSite(cfg, base);
284
- writeCard(ORIGIN, { fromBlock, repos: [] });
290
+ writeCard(view.ORIGIN, { fromBlock, repos: [] });
285
291
  const gateway = process.env.FANGORN_IPFS_GATEWAY;
286
292
  w.assets = { ...w.assets, directory: relative(dirname(resolve(wranglerPath)), site) };
287
293
  w.vars = { ...w.vars, CONFIG: { app: cfg.app, name: cfg.name, description: cfg.description, fromBlock, network: cfg.network,
@@ -398,7 +404,7 @@ export async function ship(configPath, { crawl = true, deploy = true, dry = fals
398
404
  const workerConfig = (u) => {
399
405
  const path = join(dir, "wrangler.json");
400
406
  writeFileSync(path, JSON.stringify(wranglerConfig({
401
- name: cfg.site.project, site, account: cfg.site.account, cron: cfg.site.cron, url: cfg.site.url, ipfsGateway: view.ipfsGateway(),
407
+ name: cfg.site.project, site, account: cfg.site.account, cron: cfg.site.cron, cpuMs: cfg.site.cpu_ms, url: cfg.site.url, ipfsGateway: view.ipfsGateway(),
402
408
  config: { app: cfg.app, name: cfg.name, description: cfg.description, fromBlock: state.fromBlock, network: cfg.network,
403
409
  schema: { types: cfg.types, relations: cfg.relations }, repos: register ? [] : repos, paid: sale(u).sale, private: access(u).config },
404
410
  }), null, 1));
@@ -549,6 +555,15 @@ export async function buildSite(cfg, base) {
549
555
  if (cfg.site.pages) cpSync(resolve(base, cfg.site.pages), site, { recursive: true });
550
556
  writeFileSync(join(site, "nav.json"), JSON.stringify(cfg.site.nav ?? []));
551
557
  writeFileSync(join(site, "examples.json"), JSON.stringify(cfg.site.examples ?? []));
558
+ // site.skill: the app's agent skill (a SKILL.md), served at skill/<app>/SKILL.md and named in the
559
+ // card with its hash, so fangorn-index can offer it to whoever opens the app.
560
+ let skill = null;
561
+ if (cfg.site.skill) {
562
+ const bytes = readFileSync(resolve(base, cfg.site.skill));
563
+ mkdirSync(join(site, "skill", cfg.app), { recursive: true });
564
+ writeFileSync(join(site, "skill", cfg.app, "SKILL.md"), bytes);
565
+ skill = { path: `skill/${cfg.app}/SKILL.md`, sha256: createHash("sha256").update(bytes).digest("hex") };
566
+ }
552
567
  // The card lists the tools the page at `url` registers: the stock page's, or the app's own.
553
568
  const card = await import("../agent/agent-card.js");
554
569
  const { registerAgent } = await import(cfg.site.agent ? pathToFileURL(resolve(base, cfg.site.agent)).href : "../../site/agent.js");
@@ -564,7 +579,9 @@ export async function buildSite(cfg, base) {
564
579
  ...(paid ? [{ id: "keys", name: "Keys", tags: [...(cfg.tags ?? []), "x402"],
565
580
  description: `The keys to sealed records (POST ${u}/paid), per record.` }] : []),
566
581
  ],
567
- fangorn: { app: cfg.app, fromBlock, repos, views: [`${u}/view`], mcp: `${u}/mcp`, config, ...(paid ? { paid } : {}), ...(access ? { access } : {}) },
582
+ fangorn: { app: cfg.app, fromBlock, repos, views: [`${u}/view`], mcp: `${u}/mcp`, config, ...(paid ? { paid } : {}), ...(access ? { access } : {}),
583
+ ...(skill ? { skill: { url: `${u}/${skill.path}`, sha256: skill.sha256 } } : {}),
584
+ ...(cfg.site.plugin ? { plugin: cfg.site.plugin } : {}) },
568
585
  });
569
586
  mkdirSync(join(site, ".well-known"), { recursive: true });
570
587
  writeFileSync(join(site, ".well-known/agent-card.json"), JSON.stringify(agentCard, null, 1));
@@ -703,8 +720,11 @@ export async function indexHere(configPath, { port = 8791 } = {}) {
703
720
  if (secret) writeFileSync(vars, `PAID_SECRET=${secret}\n`, { mode: 0o600 });
704
721
  const url = `http://127.0.0.1:${port}`, start = Math.floor(Date.now() / 1000);
705
722
  const wrangler = spawn("npx", ["wrangler", "dev", "--config", wpath, "--port", String(port), "--ip", "127.0.0.1", "--test-scheduled"],
706
- { cwd: join(HERE, "../.."), stdio: ["ignore", "inherit", "inherit"],
723
+ { cwd: join(HERE, "../.."), stdio: ["ignore", "pipe", "inherit"],
707
724
  env: { ...process.env, ...(cfg.site.account ? { CLOUDFLARE_ACCOUNT_ID: cfg.site.account } : {}) } });
725
+ // A run already holds the lock (the deployed cron's): this one skips, and the catalog would never move.
726
+ let skipped = false;
727
+ wrangler.stdout.on("data", (b) => { process.stdout.write(b); if (String(b).includes("index: a run is in progress; skipped")) skipped = true; });
708
728
  try {
709
729
  for (;;) {
710
730
  if (wrangler.exitCode !== null) throw new Error("wrangler dev exited");
@@ -716,6 +736,7 @@ export async function indexHere(configPath, { port = 8791 } = {}) {
716
736
  let catalog = null;
717
737
  while (!(catalog?.generated_at >= start)) {
718
738
  if (wrangler.exitCode !== null) throw new Error("wrangler dev exited");
739
+ if (skipped) throw new Error("another indexer run holds the lock (the deployed cron's, most likely); it frees within 2 minutes of that run dying. Try again then.");
719
740
  await new Promise((ok) => setTimeout(ok, 5000));
720
741
  catalog = await fetch(`${url}/view/cdn/catalog`).then((r) => (r.ok ? r.json() : null), () => null);
721
742
  }