@fangorn-network/westmarch 2026.10.29 → 2026.11.4-dev

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 (360) hide show
  1. package/README.md +5 -5
  2. package/docs/app-to-agent.md +61 -625
  3. package/package.json +12 -17
  4. package/site/dist/assets/__vite-browser-external-D2U8RZpp.js +1 -0
  5. package/site/dist/assets/embed.worker-CmS57Zti.js +1 -0
  6. package/site/dist/assets/index-Djs8f08m.js +8 -0
  7. package/site/dist/assets/rolldown-runtime-COiEfhsA.js +1 -0
  8. package/site/dist/assets/{transformers.web-BTyOEJ0C.js → transformers.web-CwR_IYQV.js} +1 -1
  9. package/site/dist/index.html +3 -4
  10. package/site/worker.js +115 -338
  11. package/src/agent/agent-card.js +33 -45
  12. package/src/agent/mcp.js +168 -371
  13. package/src/agent/tools.js +2 -1
  14. package/src/agent/view-tools.js +4 -56
  15. package/src/agent/x402.js +182 -369
  16. package/src/core/roles.js +6 -6
  17. package/src/core/shard.js +4 -4
  18. package/src/discover/apps.js +67 -81
  19. package/src/discover/directory.js +2 -2
  20. package/src/market/reactions.js +21 -15
  21. package/src/publish/devnet.js +69 -18
  22. package/src/publish/init.js +1 -1
  23. package/src/publish/ship.js +74 -275
  24. package/src/publish/view.js +52 -53
  25. package/template/README.md +9 -11
  26. package/template/package.json +1 -1
  27. package/plugin/scripts/fx.mjs +0 -266
  28. package/site/dist/assets/AccountNotFoundScreen-C4njXc69-DyVH4CEz.js +0 -1
  29. package/site/dist/assets/AddFundsSelectionScreen-RAoCn7ej-Dz1--SS9.js +0 -31
  30. package/site/dist/assets/Address-H8kD1d9O-BdFZmRwI.js +0 -17
  31. package/site/dist/assets/AffirmativeConsentScreen-BG0MJxku-CxaCAisL.js +0 -6
  32. package/site/dist/assets/AllowlistRejectionScreen-Cae6e1Od-JGk7vndN.js +0 -1
  33. package/site/dist/assets/ApiController-CTLbMCcH.js +0 -5
  34. package/site/dist/assets/ArrowsRightLeftIcon-B8g9adpz.js +0 -1
  35. package/site/dist/assets/AuthenticateWithWalletScreen-CU6hIcFT-CXD9_OP8.js +0 -1
  36. package/site/dist/assets/AwaitingEvmToSolBridgingScreen-D0e_sZMS-BtJCglvU.js +0 -1
  37. package/site/dist/assets/AwaitingExternalEthereumTransferScreen-ur5HL4R7-BgC2TsqH.js +0 -1
  38. package/site/dist/assets/AwaitingPasswordlessCodeScreen-DRHz76mM-B9OzLeEO.js +0 -103
  39. package/site/dist/assets/AwaitingSolToEvmBridgingScreen-CdLRrGD2-C7r5snUL.js +0 -1
  40. package/site/dist/assets/BridgeNetworkSelectionView-CFGjB2nO-BTV0NRio.js +0 -68
  41. package/site/dist/assets/CaptchaScreen-DbpvkIGx-7rSZIuM-.js +0 -1
  42. package/site/dist/assets/CheckCircleIcon-B1l17ioc.js +0 -1
  43. package/site/dist/assets/CheckCircleIcon-BGSI8nKf.js +0 -1
  44. package/site/dist/assets/Checkbox-D1EDeo41-BUfq0w99.js +0 -52
  45. package/site/dist/assets/ChevronDownIcon-B6uJvPHu.js +0 -1
  46. package/site/dist/assets/Chip-CZKIKt9K-r9Nk__0-.js +0 -26
  47. package/site/dist/assets/CoinbaseOnrampStatusScreen-BbyCJDXL-DWWNHxmR.js +0 -16
  48. package/site/dist/assets/ConnectEmailForm-B-VRE0MN-CRQwS0Xu.js +0 -3
  49. package/site/dist/assets/ConnectLedgerScreen-DWHABGot-CZcCt31O.js +0 -10
  50. package/site/dist/assets/ConnectOnlyLandingScreen-CZYx9Bmp-C5IOf-SQ.js +0 -1
  51. package/site/dist/assets/ConnectOnlyStatusScreen-DCRzjxHx-cwg74F3A.js +0 -1
  52. package/site/dist/assets/ConnectOrCreateScreen-CUUYUO07-D28oeK6b.js +0 -1
  53. package/site/dist/assets/ConnectPhoneForm-Dek5xn4C-EEdkKeTp.js +0 -95
  54. package/site/dist/assets/ConnectWalletView-DqI-PTvX-C2mEzukj.js +0 -74
  55. package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-CH_7r8iV.js +0 -7
  56. package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-Z33kis0B.js +0 -1
  57. package/site/dist/assets/ConnectorUtil-DH4NePk5.js +0 -21
  58. package/site/dist/assets/ConstantsUtil-DmLsRHAl.js +0 -198
  59. package/site/dist/assets/CopyToClipboard-i_OQSBJr-_G88JCVR.js +0 -39
  60. package/site/dist/assets/CopyableText-CQapvaMr-CGGfc_zM.js +0 -28
  61. package/site/dist/assets/CrossAppAuthScreen-CLw7ymZq-C9Okoc0V.js +0 -1
  62. package/site/dist/assets/CustomLandingScreenView-Cm_MBSyX-D2l6hdSl.js +0 -22
  63. package/site/dist/assets/DelegatedActionsConsentScreen-DYF2U4y5-Dda3KKqS.js +0 -1
  64. package/site/dist/assets/DelegatedActionsRevokeScreen-bPp6Uwyh-DG8kJPt1.js +0 -1
  65. package/site/dist/assets/DepositAddressAddressStepView-4vdkb55L-CcOUfn6f.js +0 -187
  66. package/site/dist/assets/DepositAddressScreen-DBLRs7aI-DeituDNh.js +0 -8
  67. package/site/dist/assets/DepositAddressV3Screen-CDqIEMQw-FDg6K14-.js +0 -1
  68. package/site/dist/assets/EmailInputForm-Cqo1mda8-BmbxvPHC.js +0 -124
  69. package/site/dist/assets/EmbeddedWalletConnectingScreen-D4T7DUzs-B05CQ9rx.js +0 -1
  70. package/site/dist/assets/EmbeddedWalletCreatedScreen-D7WhW5ve-Cxd1b9XH.js +0 -1
  71. package/site/dist/assets/EmbeddedWalletKeyExportScreen-CqV4NYQ3-CrJy0ViX.js +0 -23
  72. package/site/dist/assets/EmbeddedWalletOnAccountCreateScreen-BoKb3Q2S-Bakaa3zu.js +0 -1
  73. package/site/dist/assets/EmbeddedWalletPasswordUpdateScreen-D3rMlqS7-j78vKrcj.js +0 -1
  74. package/site/dist/assets/EmbeddedWalletPasswordUpdateSplashScreen-BPFkWJeE-D3dr7W9_.js +0 -1
  75. package/site/dist/assets/EnrollTotp-Dibew4dp-BXbYV8nI.js +0 -66
  76. package/site/dist/assets/EnvelopeIcon-DjPofnRI.js +0 -1
  77. package/site/dist/assets/ErrorBanner-BcpGRt0h-BhH6_V-9.js +0 -21
  78. package/site/dist/assets/ErrorMessage-D8VaAP5m-Bs7uX2a9.js +0 -8
  79. package/site/dist/assets/ErrorScreen-DipdTWzE-BgPRgnOb.js +0 -4
  80. package/site/dist/assets/ErrorScreen-DipdTWzE-CvxXC6pz.js +0 -1
  81. package/site/dist/assets/ExclamationCircleIcon-BE-WGrut.js +0 -1
  82. package/site/dist/assets/ExclamationTriangleIcon-Ca1Xlc_f.js +0 -1
  83. package/site/dist/assets/FarcasterConnectStatusScreen-51tjUxqq-De2JvZdV.js +0 -75
  84. package/site/dist/assets/FarcasterSignerStatusScreen-6x-Uh95G-bLp3hXoO.js +0 -28
  85. package/site/dist/assets/FiatOnrampScreen-BAhYeiTI-Jr6D6JiZ.js +0 -491
  86. package/site/dist/assets/FingerPrintIcon-DOCbS93y.js +0 -1
  87. package/site/dist/assets/FundSolWalletWithExternalSolanaWallet-WtLwYaOl-A_G5kfa1.js +0 -1
  88. package/site/dist/assets/FundWalletMethodHeader-BWNkokCT-LPv35wkD.js +0 -1
  89. package/site/dist/assets/FundWithBankDepositScreen-DajkHxBD-0hfGrfvf.js +0 -58
  90. package/site/dist/assets/FundingEditAmountScreen-CpvpiO4M-Dz0s_NWR.js +0 -1
  91. package/site/dist/assets/FundingMethodSelectionScreen-Db7vhRlE-ZGm-3-Io.js +0 -1
  92. package/site/dist/assets/GlobeAltIcon-BBRl2MMn.js +0 -1
  93. package/site/dist/assets/GooglePay-B53WnudL-BGiVpB8y.js +0 -1
  94. package/site/dist/assets/HCaptchaWrapper-CUi9LJsp-CHdfIeD_.js +0 -4
  95. package/site/dist/assets/InAppBrowserLoginNotPossible-BMR9br1T-Cgoiow3m.js +0 -1
  96. package/site/dist/assets/InfoBanner-Cb3p1z12-_YqQmRiu.js +0 -22
  97. package/site/dist/assets/InjectedWalletIcon-DLcYOGDj-tuIrKQHH.js +0 -1
  98. package/site/dist/assets/InstallWalletScreen-DTA8dwLv-BMU_wa61.js +0 -1
  99. package/site/dist/assets/JsonTree-BHzNC-ic-DcVr0YF0.js +0 -34
  100. package/site/dist/assets/LabelXs-oqZNqbm_-CNprGVQM.js +0 -6
  101. package/site/dist/assets/LandingScreen-CGWUoSUG-fH6rsQen.js +0 -1
  102. package/site/dist/assets/Layouts-BMRfo5hw-e-sepi6F.js +0 -77
  103. package/site/dist/assets/Link-BdDilT2T-BdwlRJch.js +0 -40
  104. package/site/dist/assets/LinkConflictScreen-C3ZEPsgl-BXKN2Qm5.js +0 -77
  105. package/site/dist/assets/LinkEmailScreen-dKo9frwc-Cyn7Q8p1.js +0 -1
  106. package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-CWiqnwLM.js +0 -117
  107. package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-YmDevJzf.js +0 -1
  108. package/site/dist/assets/LinkPhoneScreen-oIUpfj0w-h7shW-Ay.js +0 -1
  109. package/site/dist/assets/Loader-UAZqxsnw-DJEVblm1.js +0 -62
  110. package/site/dist/assets/LoadingSkeleton-BMsgO5PV-D2atjsP5.js +0 -17
  111. package/site/dist/assets/LockClosedIcon-9EFzWK1C.js +0 -1
  112. package/site/dist/assets/LoginFailedScreen-CN0dJV6U-By3Si77a.js +0 -1
  113. package/site/dist/assets/ManualTransferScreen-CHqcuqbs-BAHPY8rs.js +0 -1
  114. package/site/dist/assets/MfaAuthEnrollmentFlowScreen-Dj7gPTRC-CLjffFG1.js +0 -1
  115. package/site/dist/assets/MfaAuthVerifyFlowScreen-DbFpyWMy-BmQg1Y61.js +0 -1
  116. package/site/dist/assets/MfaEnrollmentFlowScreen-DhLnGgnr-DmBtTtlw.js +0 -1
  117. package/site/dist/assets/MfaVerifyFlowScreen-C_XPjMRP-4a683Bmf.js +0 -1
  118. package/site/dist/assets/ModalFooter-BCuEK_nz-C_71qsaz.js +0 -403
  119. package/site/dist/assets/MoonpayStatusScreen-CzTUtokc-DEHHcEf8.js +0 -16
  120. package/site/dist/assets/OAuthStatusScreen-ufbOa-da-BjAb_WsY.js +0 -1
  121. package/site/dist/assets/OpenLink-CUpJ1mOr-BLWFsPOW.js +0 -39
  122. package/site/dist/assets/PasskeySelectSignupOrLogin-xh2ScC_l-2PJTJxX-.js +0 -1
  123. package/site/dist/assets/PasskeyStatusScreen-Bla8MaOk-DCEg761l.js +0 -6
  124. package/site/dist/assets/PasswordRecoveryScreen-awGXZkQk-8nVjNc8k.js +0 -20
  125. package/site/dist/assets/PhArrowCircleDown-D97G-BOA.js +0 -14
  126. package/site/dist/assets/PhArrowClockwise-DVgyFuez.js +0 -14
  127. package/site/dist/assets/PhArrowDown-87b1BizM.js +0 -14
  128. package/site/dist/assets/PhArrowLeft-Da0MfnbK.js +0 -14
  129. package/site/dist/assets/PhArrowRight-AdrIL4DW.js +0 -14
  130. package/site/dist/assets/PhArrowSquareOut-Bk2CMG3I.js +0 -14
  131. package/site/dist/assets/PhArrowUp-4sgyKPGx.js +0 -14
  132. package/site/dist/assets/PhArrowUpRight-BNghD2zq.js +0 -14
  133. package/site/dist/assets/PhArrowsClockwise-BXRn1GEl.js +0 -14
  134. package/site/dist/assets/PhArrowsDownUp-DM09Jo_3.js +0 -14
  135. package/site/dist/assets/PhArrowsLeftRight-MuKBLUoF.js +0 -14
  136. package/site/dist/assets/PhBank-fHbgCqqZ.js +0 -14
  137. package/site/dist/assets/PhBrowser-2-9m-AkM.js +0 -14
  138. package/site/dist/assets/PhCaretDown-BDNabFuL.js +0 -14
  139. package/site/dist/assets/PhCaretLeft-CI460R-A.js +0 -14
  140. package/site/dist/assets/PhCaretRight-D7ZS3Ls8.js +0 -14
  141. package/site/dist/assets/PhCaretUp-C-iIfyvO.js +0 -14
  142. package/site/dist/assets/PhCheck-DxQXn57r.js +0 -14
  143. package/site/dist/assets/PhCircleHalf-MJZeRBGF.js +0 -14
  144. package/site/dist/assets/PhClock-x3MS3Mwi.js +0 -14
  145. package/site/dist/assets/PhCompass-hYn3HhGN.js +0 -14
  146. package/site/dist/assets/PhCopy-CWNYcABI.js +0 -14
  147. package/site/dist/assets/PhCreditCard-Fy4pzwtt.js +0 -14
  148. package/site/dist/assets/PhCurrencyDollar-BgiaenCi.js +0 -14
  149. package/site/dist/assets/PhDesktop-CxrzCSv5.js +0 -14
  150. package/site/dist/assets/PhDeviceMobile-CsyA5Goi.js +0 -14
  151. package/site/dist/assets/PhDotsThree-D-k1BiYg.js +0 -14
  152. package/site/dist/assets/PhEnvelope-D8kzeNox.js +0 -14
  153. package/site/dist/assets/PhFunnelSimple-CfQs5DAW.js +0 -14
  154. package/site/dist/assets/PhGlobe-DvmSc9_A.js +0 -14
  155. package/site/dist/assets/PhIdentificationCard-CfkZm-5K.js +0 -14
  156. package/site/dist/assets/PhImage-Cf0J2ZfJ.js +0 -14
  157. package/site/dist/assets/PhInfo-Bwi51-2X.js +0 -14
  158. package/site/dist/assets/PhLightbulb-C4FZV7Es.js +0 -14
  159. package/site/dist/assets/PhMagnifyingGlass-C9u8jSCJ.js +0 -14
  160. package/site/dist/assets/PhPaperPlaneRight-zxQVvPLX.js +0 -14
  161. package/site/dist/assets/PhPlus-C4UoW-2n.js +0 -14
  162. package/site/dist/assets/PhPower-CFyWDmgD.js +0 -14
  163. package/site/dist/assets/PhPuzzlePiece-BjoNVfST.js +0 -14
  164. package/site/dist/assets/PhQrCode-CP3tFauY.js +0 -14
  165. package/site/dist/assets/PhQuestion-wOWJBd3G.js +0 -14
  166. package/site/dist/assets/PhQuestionMark-CV0k9C0Y.js +0 -14
  167. package/site/dist/assets/PhSealCheck-vIKzREuN.js +0 -14
  168. package/site/dist/assets/PhSignOut-CxELV-i0.js +0 -14
  169. package/site/dist/assets/PhSpinner-CGDmbzUd.js +0 -14
  170. package/site/dist/assets/PhTrash-C9PD0SAt.js +0 -14
  171. package/site/dist/assets/PhUser-0sWNXQeZ.js +0 -14
  172. package/site/dist/assets/PhVault-6JTskUcC.js +0 -14
  173. package/site/dist/assets/PhWarning-CoKs3gz6.js +0 -14
  174. package/site/dist/assets/PhWarningCircle-BXHw1fCt.js +0 -14
  175. package/site/dist/assets/PhX-DX0SFIqC.js +0 -14
  176. package/site/dist/assets/PhoneIcon-CZ47md80.js +0 -1
  177. package/site/dist/assets/PinInput-DbZ0b1i1-BrTXu3Km.js +0 -247
  178. package/site/dist/assets/PrivyPluginContext-DA-yS5vO-CY60yubk.js +0 -1
  179. package/site/dist/assets/QrCode-DbnZwlkH-CcMrWV5D.js +0 -22
  180. package/site/dist/assets/RecoveryOAuthStatusScreen-CsgtTI2x-DgeDnHOK.js +0 -1
  181. package/site/dist/assets/RecoveryPasswordCreateScreen-Be-qJ-LU-CvJJwLhK.js +0 -1
  182. package/site/dist/assets/RecoverySelectionScreen-D1iGtpmi-CADtGtSV.js +0 -7
  183. package/site/dist/assets/SIWXUtil-Cpiv91-M.js +0 -1
  184. package/site/dist/assets/Screen-D30meWsY-vEusJ7KX.js +0 -175
  185. package/site/dist/assets/ScreenHeader-CHmc4-Lu-BvGTLg53.js +0 -31
  186. package/site/dist/assets/ScreenLayout-OwaQQcxH-cfOTmyl0.js +0 -1
  187. package/site/dist/assets/SelectSourceAsset-CWFog6gm-DxYklZYs.js +0 -140
  188. package/site/dist/assets/SetAutomaticRecoveryScreen-BO_OEqAZ-oeqeo05x.js +0 -14
  189. package/site/dist/assets/SetWalletPasswordForm-DwCTVtAh-BxFee9UA.js +0 -11
  190. package/site/dist/assets/ShieldCheckIcon-eFjsosCA.js +0 -1
  191. package/site/dist/assets/SignRequestScreen-BHKv32KR-z8ixfIma.js +0 -26
  192. package/site/dist/assets/SolanaWalletConnector-C7Lqk-Wz-Bc_lvdiF.js +0 -1
  193. package/site/dist/assets/StackedContainer-B2vaEl56-Do5dXB8-.js +0 -24
  194. package/site/dist/assets/StandardSignAndSendTransactionScreen-CfhIXVtd-DudetZAc.js +0 -20
  195. package/site/dist/assets/Subtitle-CV-2yKE4-DV0o1av3.js +0 -13
  196. package/site/dist/assets/TelegramAuthScreen-UL9zLfO8-Bo3sONvf.js +0 -1
  197. package/site/dist/assets/Title-BnzYV3Is-C3qOPnO_.js +0 -7
  198. package/site/dist/assets/TodoList-DnyULl18-BvxvubbF.js +0 -36
  199. package/site/dist/assets/TransactionDetails-e9nesl30-WWflkOIi.js +0 -109
  200. package/site/dist/assets/TransactionErrorView-BDBNqA_f-CotYKcgf.js +0 -178
  201. package/site/dist/assets/TransferFromWalletScreen-Qvn1Yuno-sVFlWqa8.js +0 -31
  202. package/site/dist/assets/TransferOrBridgeLoadingScreen-Dl1I_iIz-CV2-mY19.js +0 -6
  203. package/site/dist/assets/TurnstileWrapper-B7sNddys-oPbuMTQt.js +0 -1
  204. package/site/dist/assets/UpdateEmailScreen-DVOhPV0H-D_mjSpAy.js +0 -1
  205. package/site/dist/assets/UpdatePhoneScreen-CXRswTNZ-6RLG0ahv.js +0 -1
  206. package/site/dist/assets/UserLimitReachedScreen-5we2DHn8-CaVN0bti.js +0 -1
  207. package/site/dist/assets/Value-DTgR824E-Bp6WGZZm.js +0 -27
  208. package/site/dist/assets/WalletCards-DH1rqayz-D3cOTb9x.js +0 -1
  209. package/site/dist/assets/WalletIcon-DOIZhb2l.js +0 -1
  210. package/site/dist/assets/WalletInfoCard-sq_2wgFg-bOqfl5MA.js +0 -29
  211. package/site/dist/assets/WalletInterstitialScreen-Sh7LfsAh-DWaVkMCA.js +0 -1
  212. package/site/dist/assets/WalletLink-DK5LB5Ha-DjUZvqs-.js +0 -20
  213. package/site/dist/assets/WalletOverflowButton-bfCnp-9T-B6P7DQXA.js +0 -1
  214. package/site/dist/assets/WarningBanner-ZZqCEtZK-Bc2YKZb7.js +0 -21
  215. package/site/dist/assets/__vite-browser-external-NciY4Vu1.js +0 -1
  216. package/site/dist/assets/_esm-BZP3cXTv.js +0 -1
  217. package/site/dist/assets/_esm-ZmzBJpEs.js +0 -6
  218. package/site/dist/assets/accounts-D4AXF17G.js +0 -1
  219. package/site/dist/assets/accounts-IkdFx9nq.js +0 -1
  220. package/site/dist/assets/analytics-mkkvFRju-BRBcadka.js +0 -1
  221. package/site/dist/assets/baseSepolia-Bn1j1Ysw.js +0 -1
  222. package/site/dist/assets/basic-Cz1YiCSk.js +0 -1575
  223. package/site/dist/assets/capitalizeFirstLetter-DmLYqXsO-DtOXx-2n.js +0 -1
  224. package/site/dist/assets/ccip-B1J3oTZx.js +0 -7
  225. package/site/dist/assets/ccip-Bd7261zy.js +0 -1
  226. package/site/dist/assets/ccip-D6EHKpjI.js +0 -1
  227. package/site/dist/assets/chain-scefkYIH.js +0 -5
  228. package/site/dist/assets/check-DHnllI8N.js +0 -1
  229. package/site/dist/assets/chevron-down-1y-r4bGO.js +0 -1
  230. package/site/dist/assets/circle-alert-OwxamXUX.js +0 -1
  231. package/site/dist/assets/circle-check-big-3sZGVC10.js +0 -1
  232. package/site/dist/assets/circle-x-DQjNUV9J.js +0 -1
  233. package/site/dist/assets/client-B-BpRtwY.js +0 -1
  234. package/site/dist/assets/client-BfIR1FvH.js +0 -9
  235. package/site/dist/assets/coinbase-DCPC2L_R-YEuIn3jh.js +0 -3
  236. package/site/dist/assets/copy-DMOX_yn7.js +0 -1
  237. package/site/dist/assets/core-GIEmxmAa.js +0 -12
  238. package/site/dist/assets/createLucideIcon-BsERoFTA.js +0 -1
  239. package/site/dist/assets/defineChain-B-SQDLTG.js +0 -1
  240. package/site/dist/assets/dijkstra-CY95afk4.js +0 -1
  241. package/site/dist/assets/dist-CoS6E-3x.js +0 -1
  242. package/site/dist/assets/dist-Cp6IXeJd.js +0 -1
  243. package/site/dist/assets/dist-CrNxmdZj.js +0 -1
  244. package/site/dist/assets/dist-DYGtf4F0.js +0 -16
  245. package/site/dist/assets/embed.worker-Cnl4VGr3.js +0 -1
  246. package/site/dist/assets/errors-BQ3N-RVt-CZPAWCgC.js +0 -1
  247. package/site/dist/assets/esm-C3DslVJx.js +0 -1
  248. package/site/dist/assets/esm-Ci1ogKuK.js +0 -1
  249. package/site/dist/assets/esm-Co94ENUV.js +0 -37
  250. package/site/dist/assets/esm-bb8SANZh.js +0 -1
  251. package/site/dist/assets/esm-n2lWo7kz.js +0 -1
  252. package/site/dist/assets/ethers-zTjbodk5-Cs648CnD.js +0 -1
  253. package/site/dist/assets/eventemitter3-Dzc_0Uq2.js +0 -1
  254. package/site/dist/assets/external-link-BBPG3rG7.js +0 -1
  255. package/site/dist/assets/farcaster-DPlSjvF5-CzBCmgyL.js +0 -1
  256. package/site/dist/assets/features-CuiHbgEJ.js +0 -2
  257. package/site/dist/assets/fingerprint-pattern-CjLtyIxB.js +0 -1
  258. package/site/dist/assets/floating-ui.react-DmaCfayr.js +0 -1
  259. package/site/dist/assets/floating-ui.react-dom-CaoMF8mP.js +0 -1
  260. package/site/dist/assets/formatErc20TokenAmount-BuPk9xcy-BVqOKQif.js +0 -1
  261. package/site/dist/assets/formatter-CVbp4SlJ.js +0 -1
  262. package/site/dist/assets/get-entropy-details-for-user-dHRH5Gpi-BNiOFXbK.js +0 -1
  263. package/site/dist/assets/getChainName-DjpPdUSc-DUyKDrnp.js +0 -1
  264. package/site/dist/assets/getErc20Balance-jXNpFf0N-q4bV8kQq.js +0 -1
  265. package/site/dist/assets/getErc20TokenInfo-BNTrzLMP-DcnTFrXN.js +0 -1
  266. package/site/dist/assets/getFormattedUsdFromLamports-De3U9GlO-Gx4_wxON.js +0 -1
  267. package/site/dist/assets/getUsdcMintAddress-BVgm9GRo-r2UnDeEy.js +0 -1
  268. package/site/dist/assets/guards-BIW-GiMJ-D3tWTQAR.js +0 -1
  269. package/site/dist/assets/hooks.module-BYZGfwDl.js +0 -1
  270. package/site/dist/assets/hourglass-D_GfQdJr.js +0 -1
  271. package/site/dist/assets/id-UqEwL2BI-CTlvRMA5.js +0 -1
  272. package/site/dist/assets/index-BdEw5wyl-kLbhqLbv.js +0 -1
  273. package/site/dist/assets/index-C2h3EneX.js +0 -8
  274. package/site/dist/assets/index-CWARkn2w-DxuckIFn.js +0 -61
  275. package/site/dist/assets/index-DvMddJcd-CNBb4VUn.js +0 -12
  276. package/site/dist/assets/index-YiCw0Y_x-Czl0tWfF.js +0 -436
  277. package/site/dist/assets/index.browser-B-i-Lj3J.js +0 -1
  278. package/site/dist/assets/index.browser-BMmUcykv.js +0 -1
  279. package/site/dist/assets/index.es-Crd0TyUS.js +0 -2
  280. package/site/dist/assets/index.es-DQbup1WG.js +0 -3
  281. package/site/dist/assets/isPaymentRequestAvailable-Bq1cemEn-Cjf3l05I.js +0 -1
  282. package/site/dist/assets/jsx-runtime-CndCZlyt.js +0 -1
  283. package/site/dist/assets/landmark-BZPqMFEf.js +0 -1
  284. package/site/dist/assets/lib-DGtzi2DK.js +0 -1
  285. package/site/dist/assets/localBatchGatewayRequest-B71J8sQ9.js +0 -29
  286. package/site/dist/assets/lock-DyN_c-ud.js +0 -1
  287. package/site/dist/assets/logger-CsXm3oIv-B0UYpNFI.js +0 -1
  288. package/site/dist/assets/mail-V_mcPb64.js +0 -1
  289. package/site/dist/assets/modal-context-CwmSeFcv-DPWNJrfK.js +0 -1
  290. package/site/dist/assets/native-BKj27wfc.js +0 -1
  291. package/site/dist/assets/ofetch.CWycOUEr-Duby3iHT.js +0 -1
  292. package/site/dist/assets/parseAbi-Csg-8t59.js +0 -2
  293. package/site/dist/assets/parseAbiParameters-BWbIMYe4.js +0 -1
  294. package/site/dist/assets/parseEther-CHS0GNkM.js +0 -1
  295. package/site/dist/assets/pkce-DXF0yKjP-7_VY-d83.js +0 -1
  296. package/site/dist/assets/preload-helper-CRzLMJPS.js +0 -1
  297. package/site/dist/assets/privy-context-81KmsBw2-DiR6-Yq8.js +0 -2
  298. package/site/dist/assets/property-DuZ9DLP4.js +0 -3
  299. package/site/dist/assets/react-BfYA1q0G.js +0 -1
  300. package/site/dist/assets/react-CK2HRI1t.js +0 -1
  301. package/site/dist/assets/react-DxGeY8B8.js +0 -1
  302. package/site/dist/assets/react-auth-sHnipzbS.js +0 -1
  303. package/site/dist/assets/react-dom-BI2Koe4o.js +0 -1
  304. package/site/dist/assets/reservoir-x-nGuZkT-BguVFH3D.js +0 -1
  305. package/site/dist/assets/rolldown-runtime-Bjp52BOI.js +0 -1
  306. package/site/dist/assets/safe-url-D7SRPu33-CkMedGro.js +0 -1
  307. package/site/dist/assets/secp256k1-BeYL8aZl.js +0 -1
  308. package/site/dist/assets/secp256k1-Bh4MSkhz.js +0 -1
  309. package/site/dist/assets/secp256k1-Ci4cqHl0.js +0 -1
  310. package/site/dist/assets/secp256k1-DsxXqbXv.js +0 -1
  311. package/site/dist/assets/secp256k1-N9RUmafO.js +0 -1
  312. package/site/dist/assets/serializeAuthorizationList-DGMik9Wx.js +0 -1
  313. package/site/dist/assets/serializeSignature-CWKpxoab.js +0 -2
  314. package/site/dist/assets/sha2-C6s7zZmb.js +0 -1
  315. package/site/dist/assets/shared-DRZUmnL5-BewE5wK8.js +0 -193
  316. package/site/dist/assets/shared-FM0rljBt-CxLACri_.js +0 -21
  317. package/site/dist/assets/shouldProceedtoEmbeddedWalletCreationFlow-B3vyavrh-BkAKsRNi.js +0 -1
  318. package/site/dist/assets/simulateTransaction-CfuOrae4-PjA_tGjX.js +0 -1
  319. package/site/dist/assets/smartphone-3p8nFSTE.js +0 -1
  320. package/site/dist/assets/src-BBjkfi-Q.js +0 -1
  321. package/site/dist/assets/storage-Ufl800Ne-1qCR2JMO.js +0 -8
  322. package/site/dist/assets/stringify-DqUj9VzO.js +0 -20
  323. package/site/dist/assets/stripe.esm-DEi2D291.js +0 -2
  324. package/site/dist/assets/styles-BsotlekN-Aq27nZGc.js +0 -9
  325. package/site/dist/assets/styles-Cm9H63WO-p_ic_dxE.js +0 -92
  326. package/site/dist/assets/styles-DVyDvTdj-CjsWqAld.js +0 -74
  327. package/site/dist/assets/styles-hslXy6xG-D5QHIrJU.js +0 -164
  328. package/site/dist/assets/telegram-B-JqnkqZ-Dqkm4rDn.js +0 -1
  329. package/site/dist/assets/to-ui-error-DWe-qwS3-Bi1HUQI9.js +0 -4
  330. package/site/dist/assets/toViemAccount-B5iDGq1b-CdFZwv07.js +0 -2
  331. package/site/dist/assets/transaction-BNTP-bFm-M5tJx8vY.js +0 -1
  332. package/site/dist/assets/transfer-6YztDh-t-BVQkTvG-.js +0 -1
  333. package/site/dist/assets/triangle-alert-BG3Qw-Hq.js +0 -1
  334. package/site/dist/assets/twitch-5IOe4sIQ-CaXHFlAu.js +0 -1
  335. package/site/dist/assets/types-4CuOD23i.js +0 -1
  336. package/site/dist/assets/use-headless-crypto-deposit-Yn-QRLB3-CQqeyhGY.js +0 -1
  337. package/site/dist/assets/use-unlink-wallet-BKK4_5Ur-C0xQN_Lp.js +0 -1
  338. package/site/dist/assets/useActiveWallet-B-yZYGc6-BkK6rFBm.js +0 -123
  339. package/site/dist/assets/useGetSolPrice-x7gfUIHJ-CLVXqv6I.js +0 -1
  340. package/site/dist/assets/useGetTokenPrice-CaoUkb79-DAXXs3rs.js +0 -1
  341. package/site/dist/assets/useI18n-C-5n_noF-CGpkgIVy.js +0 -1
  342. package/site/dist/assets/useWalletBalance-CZnP5f_f-Cfd64m4n.js +0 -1
  343. package/site/dist/assets/useWallets-X1ndvJ_U-CAYqg7Za.js +0 -1
  344. package/site/dist/assets/utils-BkR7EQjg.js +0 -1
  345. package/site/dist/assets/utils-DJhBNOZA.js +0 -1
  346. package/site/dist/assets/utils-DdckVfQr.js +0 -1
  347. package/site/dist/assets/w3m-modal-B4H_uiPH.js +0 -1174
  348. package/site/dist/assets/wallet-BNI12IYk.js +0 -1
  349. package/site/dist/assets/wallet-CITSaPlB.js +0 -2
  350. package/site/dist/assets/wallet-connect-BvJtUsDt-CVlSNIXP.js +0 -1
  351. package/site/dist/assets/walletconnect-registry-xxG8XLCb-BpzpBq5s.js +0 -1
  352. package/site/dist/assets/walletconnect-v2-3frQFwbg-CzZ6oybX.js +0 -12
  353. package/site/dist/assets/wui-ux-by-reown-XyHdtV1O.js +0 -1147
  354. package/site/dist/assets/x-gjDrY2rk.js +0 -1
  355. package/src/discover/explore.js +0 -291
  356. package/src/market/settle.js +0 -165
  357. package/src/market/terms.js +0 -200
  358. package/src/publish/cli.js +0 -336
  359. package/src/publish/envelope.js +0 -185
  360. package/src/publish/manifest.js +0 -142
@@ -3,7 +3,7 @@
3
3
  //
4
4
  // westmarch-ship app.json [--no-crawl] [--no-deploy] [--dry-run]
5
5
  // westmarch-ship app.json --local <out> [--crawl] a view from staged records, no chain (see buildLocal)
6
- // westmarch-ship app.json --register the app alone: claim, empty site, card bound
6
+ // westmarch-ship app.json --register the app alone: its repo, empty site, card bound
7
7
  // westmarch-ship app.json --devnet [--only …] the whole app on this machine (see devnetShip)
8
8
  // westmarch-ship app.json --index the deployed app's indexer, once, here (see indexHere)
9
9
  //
@@ -12,17 +12,16 @@
12
12
  // (/mcp) and its site. Nothing is embedded here. Every step checks the chain (or
13
13
  // Cloudflare) first and only acts on a difference, so re-running is how you update.
14
14
  //
15
- // 1. claim the app (first run only; the claim block becomes the card's fromBlock).
16
- // The claimer is the app's first member, so its own updates are accepted.
15
+ // 1. the app's repo, `<your wallet> : app` (first run only; that block becomes the card's fromBlock)
17
16
  // 2. run each source, which crawls and publishes straight into the app
18
17
  // 3. the stock page, the card and _headers → .ship/site
19
18
  // 4. deploy the Worker: its R2 bucket, Workers AI, the cron, CONFIG (app.json's schema rides in it)
20
- // 5. register the ERC-8004 agent and bind the card, if the chain points elsewhere
19
+ // 5. register the ERC-8004 agent and point the repo's @card at the card, if it points elsewhere
21
20
  //
22
21
  // The Worker picks up what 2 published on its next cron run (every few minutes),
23
22
  // so the view follows the chain with nobody running anything.
24
23
  //
25
- // --register is the fast path, run before there is any data: 1, 3, 4 and 5 binding the card
24
+ // --register is the fast path, run before there is any data: 1, 3, 4 and 5 pointing at the card
26
25
  // without minting. The mint copies the card's skills into its IPFS registration file and is
27
26
  // never redone, so it waits for the first full ship, when the card lists what the app does.
28
27
  //
@@ -31,7 +30,7 @@
31
30
  // to app.json: the site, the Worker's config and a small state.json.
32
31
 
33
32
  import { spawn, spawnSync } from "node:child_process";
34
- import { createHash, randomBytes } from "node:crypto";
33
+ import { createHash } from "node:crypto";
35
34
  import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
36
35
  import { createRequire } from "node:module";
37
36
  import { dirname, join, relative, resolve } from "node:path";
@@ -39,35 +38,6 @@ import { homedir } from "node:os";
39
38
  import { fileURLToPath, pathToFileURL } from "node:url";
40
39
 
41
40
  const HERE = dirname(fileURLToPath(import.meta.url));
42
- // USDC per x402 network (CAIP-2): its address and EIP-712 domain, as each contract reports them.
43
- // They differ: Base Sepolia's is "USDC", Base's "USD Coin", and a payment signed under the wrong
44
- // one fails at the facilitator.
45
- const USDC = {
46
- "eip155:84532": { asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", name: "USDC", version: "2" },
47
- "eip155:8453": { asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", name: "USD Coin", version: "2" },
48
- "eip155:421614": { asset: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d", name: "USD Coin", version: "2" },
49
- "eip155:42161": { asset: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", name: "USD Coin", version: "2" },
50
- };
51
- const X402_NETWORK = { "base-sepolia": "eip155:84532", base: "eip155:8453", "arbitrum-sepolia": "eip155:421614", arbitrum: "eip155:42161" };
52
-
53
- /** How a sale settles, from `paid` or `private` in app.json: the network (`network`, else the
54
- * app's own), the token and its EIP-712 domain, the payee (`payTo`, else the owner) and the facilitator. */
55
- export function settlement(o, { network: own, owner }) {
56
- const network = o.network ? X402_NETWORK[o.network] ?? o.network : own;
57
- const usdc = o.asset ? {} : USDC[network];
58
- if (!usdc) throw new Error(`network ${network}: no default USDC; set asset, tokenName and tokenVersion`);
59
- const name = o.tokenName ?? usdc.name, version = o.tokenVersion ?? usdc.version;
60
- if (!name || !version) throw new Error("asset needs tokenName and tokenVersion: its EIP-712 domain");
61
- return { network, asset: o.asset ?? usdc.asset, name, version, decimals: o.decimals ?? 6, symbol: o.symbol ?? "USDC",
62
- payTo: o.payTo ?? owner, facilitator: (o.facilitator ?? "https://facilitator.payai.network").replace(/\/$/, "") };
63
- }
64
-
65
- /** "0.01" → "10000": a decimal price in the token's base units. */
66
- export function units(price, decimals = 6) {
67
- if (!/^\d+(\.\d+)?$/.test(String(price))) throw new Error(`price must be a decimal like "0.01", got ${price}`);
68
- const [whole, frac = ""] = String(price).split(".");
69
- return (BigInt(whole) * 10n ** BigInt(decimals) + BigInt((frac + "0".repeat(decimals)).slice(0, decimals) || "0")).toString();
70
- }
71
41
  const STOCK = join(HERE, "../../site/dist");
72
42
  const log = (s) => console.error(`[ship] ${s}`);
73
43
 
@@ -82,8 +52,8 @@ const log = (s) => console.error(`[ship] ${s}`);
82
52
  // "pages": "pages", // optional: the app's own files, copied over the stock page
83
53
  // // (pages/intro.html, a fragment, replaces the description on the front page)
84
54
  // "nav": [{ "href": "coverage.html", "label": "Coverage" }], // optional: header links to them
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
55
+ // "examples": ["…"], // optional: questions to start from, on the front page
56
+ // "plugin": {"marketplace": "owner/repo", "name": "…"}, // optional: its Claude Code plugin (hooks), named in its card
87
57
  // "skill": "skill/SKILL.md", // optional: the app's agent skill, served at skill/<app>/SKILL.md and named
88
58
  // // in the card with its sha256; fangorn-index offers it to whoever opens the app
89
59
  // "agent": "app/agent.js" }, // optional: the module whose registerAgent the card lists, when
@@ -94,33 +64,22 @@ const log = (s) => console.error(`[ship] ${s}`);
94
64
  // "live": ["readings"], // optional: namespaces members push to from their own wallets (devices,
95
65
  // // contributors), presented beside the sources'; true = every repo they push.
96
66
  // // Without sources the app already presents every member's repo.
97
- // "network": "baseSepolia", // required: the SDK's network name. baseSepolia for a new app; an app
98
- // // already claimed on arbitrumSepolia keeps it (a different name re-claims it there)
67
+ // "network": "baseSepolia", // required: the SDK's network name
68
+ // "owner": "0x…", // the wallet that publishes the app's repo; only `westmarch build` (the
69
+ // // deploy button) needs it, westmarch-ship asks the CLI
99
70
  // "fangorn": "fangorn", // the CLI that signs; e.g. "node ~/fangorn/fangorn/lib/cli/cli.js",
100
71
  // // or "env HOME=<dir> node …/cli.js" to sign with <dir>/.fangorn/config.json
101
- // "paid": { "price": "0.01", "description": "…" } // settles in USDC on the app's network, or "network": "base" / "eip155:8453"…
72
+ // "paid": true, // optional: the app quotes its records' prices over x402
73
+ // "facilitator": "https://…" // with paid: the x402 facilitator its quotes settle through (Barliman)
102
74
  // }
103
75
  //
104
- // `paid` sells per-record keys over x402 v2 (site/worker.js), and the price goes in the card,
105
- // /openapi.json and /.well-known/x402. The payee is the app owner unless `paid.payTo` says
106
- // otherwise; the asset is USDC on `paid.network` unless `paid.asset` (with `tokenName` and
107
- // `tokenVersion`, its EIP-712 domain) does. PAID_SECRET is a Worker secret
108
- // (`wrangler secret put PAID_SECRET`), the same value the sources encrypt with.
109
- // The facilitator (PayAI unless `paid.facilitator`) lists POST /paid in its Bazaar on the first
110
- // sale; each ship says whether it has.
111
- //
112
- // "private": { "pass": { "price": "5", "days": 30 }, "call": "0.01", "description": "…" }
113
- //
114
- // `private` makes the data itself the product. Each source stages its records (it needs an
115
- // --output-dir), ship seals every row whole (x402.js's sealRow) and commits them, so IPFS holds
116
- // only ciphertext; the Worker opens them in its cron with PAID_SECRET (made once in
117
- // .ship/secret, or $PAID_SECRET, and put to the Worker on every deploy) and serves the view and
118
- // the MCP tools only with a pass (POST /pass, x402) or one x402 payment per tool call. The card,
119
- // the catalog and `describe` stay free. Settles like `paid` (network, payTo, asset, facilitator).
76
+ // An app sells nothing itself: publishers seal and price their own records (`fangorn commit
77
+ // --paid body --price 0.01`), and the app presents them. With `paid`, the Worker answers
78
+ // GET /paid/<record id> with an x402 402 built from the record's signed terms, paid through
79
+ // Barliman (CONFIG.facilitator); the buyer then settles and gets the key from the key server.
120
80
  //
121
81
  // A source is any command that publishes into the repo `--namespace` names when given
122
- // `--publish`. It runs with FANGORN_APP_ID set, so it uploads through this app's storage
123
- // whatever the CLI's saved app is.
82
+ // `--publish`.
124
83
 
125
84
  /** `{field}`, `{field|slug}` and `{field|lower}` from a row; an argv item that is exactly
126
85
  * `{args...}` splices the row's own `args` array (for flags only some rows need). */
@@ -211,11 +170,10 @@ function run(cmd, args, { cwd, env, capture = false, dry = false, input } = {})
211
170
  const expand = (p) => p.replace(/^~(?=\/|$)/, homedir());
212
171
  const cliArgv = (cfg) => expandEnv(process.env.FANGORN_CLI ?? cfg.fangorn ?? "fangorn").split(/\s+/).filter(Boolean).map(expand);
213
172
  let CLI = ["fangorn"];
214
- const fangornCli = (app, args, opts = {}) => run(CLI[0], [...CLI.slice(1), "--app", app, ...args], { capture: true, ...opts });
173
+ const fangornCli = (args, opts = {}) => run(CLI[0], [...CLI.slice(1), ...args], { capture: true, ...opts });
215
174
 
216
175
  /** The CLI must be the SDK version westmarch reads with. A different one signs against
217
- * another deployment's contracts: its claim and commits land where nothing here looks,
218
- * and every read says the app is unclaimed. (It happened: a global 2026.08.18-dev CLI
176
+ * another deployment's contracts: its commits land where nothing here looks. (It happened: a global 2026.08.18-dev CLI
219
177
  * claimed and published onto the previous registries.) */
220
178
  function assertSameDeployment(cli) {
221
179
  // "env HOME=… node cli.js": a CLI with its own config (another wallet); find the script past the env.
@@ -235,18 +193,6 @@ function assertSameDeployment(cli) {
235
193
  "they talk to different contracts. Point app.json's \"fangorn\" at a matching CLI.");
236
194
  }
237
195
 
238
- /** The block `appId` was claimed in, or undefined: the AppRegistered log, window by window. */
239
- export async function claimBlock(chain, FangornConfig, appId) {
240
- const { parseAbiItem } = await import("viem");
241
- const from = BigInt(FangornConfig.appRegistryFromBlock), to = await chain.getBlockNumber(), step = BigInt(FangornConfig.logWindow ?? 10000);
242
- const event = parseAbiItem("event AppRegistered(bytes32 indexed app_id, address indexed owner)");
243
- for (let a = from; a <= to; a += step) {
244
- const [claimed] = await chain.getLogs({ address: FangornConfig.appRegistryContractAddress, event, args: { app_id: appId },
245
- fromBlock: a, toBlock: a + step - 1n < to ? a + step - 1n : to });
246
- if (claimed) return String(claimed.blockNumber);
247
- }
248
- }
249
-
250
196
  /** The template's app.json as `app`'s: its name, its type tag `<app>.item.v1` and the Worker. */
251
197
  export const TEMPLATE_APP = "my-app";
252
198
  export function nameApp(cfg, app) {
@@ -261,37 +207,30 @@ export function nameApp(cfg, app) {
261
207
  * `westmarch build`: what Workers Builds runs under the deploy button. The site, with a card
262
208
  * that names ORIGIN for its own URL (the Worker fills it in), and CONFIG, into the app's own
263
209
  * wrangler.json, which `wrangler deploy` then ships with the bucket, AI and cron it declares.
264
- * No chain writes and no Cloudflare calls: the owner claims the app and registers its agent
210
+ * No chain writes and no Cloudflare calls: the owner makes the repo and registers its agent
265
211
  * from fangorn.network. While app.json still names the template's app, the app is the Worker's name.
266
- *
267
- * ponytail: free apps only; a paid or private one ships with westmarch-ship, which sets its secret.
268
212
  */
269
213
  export async function buildForDeploy(configPath, wranglerPath = join(dirname(resolve(configPath)), "wrangler.json")) {
270
214
  const w = JSON.parse(readFileSync(wranglerPath, "utf8"));
271
215
  let cfg = loadConfig(configPath);
272
216
  if (cfg.app === TEMPLATE_APP) cfg = nameApp(cfg, w.name);
273
- if (cfg.paid || cfg.private) throw new Error(`${configPath}: a paid or private app ships with westmarch-ship`);
274
217
  if (!cfg.network) throw new Error(`${configPath}: "network" is required`);
218
+ if (!/^0x[0-9a-fA-F]{40}$/.test(cfg.owner ?? "")) throw new Error(`${configPath}: "owner" is required: the wallet that publishes the app's repo`);
275
219
  if (!w.r2_buckets?.[0]?.bucket_name?.includes(w.name))
276
220
  log(`the R2 bucket ${w.r2_buckets?.[0]?.bucket_name} doesn't name the Worker; two apps sharing one overwrite each other's view`);
277
221
 
278
222
  process.env.FANGORN_NETWORK = process.env.CHAIN_NAME = cfg.network;
279
- const [{ createPublicClient, http }, view] = await Promise.all([import("viem"), import("./view.js")]);
223
+ const view = await import("./view.js");
280
224
  const FangornConfig = await view.network(cfg.network);
281
- const fangorn = await view.readOnlyFangorn();
282
- fangorn.setAppId(cfg.app);
283
- if (/^0x0+$/.test(await fangorn.getAppRegistry().getAppOwner()))
284
- throw new Error(`${cfg.app} is unclaimed: claim it at fangorn.network first${/[._]/.test(cfg.app) ? "" : `, or set "app" in app.json to the name you claimed`}`);
285
- const chain = createPublicClient({ transport: http(FangornConfig.rpcUrl) });
286
- const fromBlock = cfg.fromBlock != null ? String(cfg.fromBlock) : await claimBlock(chain, FangornConfig, fangorn.getAppId());
225
+ const fromBlock = String(cfg.fromBlock ?? FangornConfig.fromBlock);
287
226
 
288
227
  const base = dirname(resolve(configPath));
289
228
  const { site, writeCard } = await buildSite(cfg, base);
290
- writeCard(view.ORIGIN, { fromBlock, repos: [] });
229
+ writeCard(view.ORIGIN, { owner: cfg.owner, fromBlock, repos: [] });
291
230
  const gateway = process.env.FANGORN_IPFS_GATEWAY;
292
231
  w.assets = { ...w.assets, directory: relative(dirname(resolve(wranglerPath)), site) };
293
232
  w.vars = { ...w.vars, CONFIG: { app: cfg.app, name: cfg.name, description: cfg.description, fromBlock, network: cfg.network,
294
- schema: { types: cfg.types, relations: cfg.relations }, repos: [], paid: null, private: null },
233
+ schema: { types: cfg.types, relations: cfg.relations }, repos: [], paid: false },
295
234
  ...(gateway ? { IPFS_GATEWAY: gateway } : {}) };
296
235
  writeFileSync(wranglerPath, JSON.stringify(w, null, 1) + "\n");
297
236
  log(`built ${cfg.app} (fromBlock ${fromBlock}) into ${site}; wrangler deploy ships it`);
@@ -301,11 +240,11 @@ export async function buildForDeploy(configPath, wranglerPath = join(dirname(res
301
240
 
302
241
  export async function ship(configPath, { crawl = true, deploy = true, dry = false, replace = false, only = null, register = false } = {}) {
303
242
  const cfg = loadConfig(configPath);
304
- // No sources is fine: the app then presents whatever its members commit (repos: [] = all of them).
305
- if (!register && !Object.keys(cfg.types).length)
243
+ // No sources is fine: the app then presents every repo on chain (repos: [] = all of them), as the explorer does.
244
+ if (!register && cfg.sources.length && !Object.keys(cfg.types).length)
306
245
  throw new Error(`${configPath}: no types yet, so nothing to build; --register sets up the app alone`);
307
- // No default: one that changed would claim every app that leaned on it again, elsewhere.
308
- if (!cfg.network) throw new Error(`${configPath}: "network" is required: "baseSepolia" for a new app, or the network the app was claimed on (before 2026-10-20 the default was "arbitrumSepolia")`);
246
+ if (!cfg.network) throw new Error(`${configPath}: "network" is required, e.g. "baseSepolia"`);
247
+ if (cfg.paid && !cfg.facilitator) throw new Error(`${configPath}: "paid" needs "facilitator", the URL of the x402 facilitator (Barliman) its quotes settle through`);
309
248
  CLI = cliArgv(cfg);
310
249
  assertSameDeployment(CLI);
311
250
  const base = dirname(resolve(configPath));
@@ -314,45 +253,28 @@ export async function ship(configPath, { crawl = true, deploy = true, dry = fals
314
253
  mkdirSync(dir, { recursive: true });
315
254
  const state = existsSync(statePath) ? JSON.parse(readFileSync(statePath, "utf8")) : {};
316
255
  const save = () => !dry && writeFileSync(statePath, JSON.stringify(state, null, 1));
317
- // A private app's PAID_SECRET: $PAID_SECRET, else made once in .ship/secret. Losing it loses
318
- // every sealed row, so it is never made again over one that exists.
319
- const secretPath = join(dir, "secret");
320
- const secret = () => {
321
- if (process.env.PAID_SECRET) return process.env.PAID_SECRET;
322
- if (!existsSync(secretPath)) writeFileSync(secretPath, randomBytes(32).toString("hex"), { mode: 0o600 });
323
- return readFileSync(secretPath, "utf8").trim();
324
- };
325
-
326
256
  // The SDK's network name: view.js reads with it, and the CLI and the sources spawned below sign on it.
327
257
  process.env.FANGORN_NETWORK = process.env.CHAIN_NAME = cfg.network;
328
258
  const [{ createPublicClient, http }, view] = await Promise.all([import("viem"), import("./view.js")]);
329
259
  const FangornConfig = await view.network(cfg.network);
330
260
  const fangorn = await view.readOnlyFangorn();
331
- fangorn.setAppId(cfg.app);
332
- const appId = fangorn.getAppId();
333
- const registry = fangorn.getAppRegistry();
334
261
  const cf = cfg.site.account ? { CLOUDFLARE_ACCOUNT_ID: cfg.site.account } : {};
335
262
 
336
- // 1. claim: pays the subscription (the app sponsors its members' storage) and makes the
337
- // claimer the app's first member.
338
- const me = (fangornCli(cfg.app, ["wallet"]).match(/Address:\s+(0x[0-9a-fA-F]{40})/) ?? [])[1]?.toLowerCase();
263
+ // 1. the app's repo: this wallet's `cfg.app`. Repo names are per publisher, so there is nothing to claim.
264
+ const me = (fangornCli(["wallet"]).match(/Address:\s+(0x[0-9a-fA-F]{40})/) ?? [])[1];
339
265
  if (!me) throw new Error("`fangorn wallet` shows no address; run `fangorn init`");
340
- let owner = (await registry.getAppOwner()).toLowerCase();
341
266
  const chain = createPublicClient({ transport: http(FangornConfig.rpcUrl) });
342
- if (/^0x0+$/.test(owner)) {
343
- const block = await chain.getBlockNumber();
344
- state.fromBlock = String(block);
345
- log(`${cfg.app} is unclaimed; claiming it for ${me} (fromBlock ${block})`);
346
- fangornCli(cfg.app, ["app", "claim"], { dry });
347
- owner = me;
267
+ const repoDir = join(dir, "repo");
268
+ if (!(await fangorn.onChainTip(me, cfg.app))) {
269
+ // Its own records start now; an app presenting every repo (the explorer) reads from the deployment's start.
270
+ if (presentedRepos(cfg).length) state.fromBlock = String(await chain.getBlockNumber());
271
+ log(`creating the app's repo ${me}:${cfg.app}`);
272
+ mkdirSync(repoDir, { recursive: true });
273
+ fangornCli(["repo", "init", cfg.app], { cwd: repoDir, dry });
348
274
  save();
349
- } else if (owner !== me) {
350
- throw new Error(`${cfg.app} belongs to ${owner}, not this wallet (${me}); pick another name`);
351
275
  }
352
- // Claimed elsewhere (fangorn.network, the CLI): the view starts at the claim, found once.
353
- if (state.fromBlock == null && cfg.fromBlock == null) state.fromBlock = await claimBlock(chain, FangornConfig, appId);
354
- state.fromBlock ??= String(cfg.fromBlock ?? FangornConfig.appRegistryFromBlock);
355
- log(`app ${cfg.app} = ${appId}, owner ${owner}, fromBlock ${state.fromBlock}`);
276
+ state.fromBlock ??= String(cfg.fromBlock ?? FangornConfig.fromBlock);
277
+ log(`app ${me}:${cfg.app}, fromBlock ${state.fromBlock}`);
356
278
 
357
279
  // 2. sources: crawl + publish straight into the app. One source failing (a site that
358
280
  // blocks us, a feed that is down) keeps its last published data and does not stop the
@@ -360,12 +282,9 @@ export async function ship(configPath, { crawl = true, deploy = true, dry = fals
360
282
  const failed = [];
361
283
  if (crawl && !register) for (const s of cfg.sources.filter((s) => !only || only.includes(s.namespace))) {
362
284
  const cwd = s.cwd ? resolve(base, s.cwd.replace(/^~/, homedir())) : base;
363
- const paidArgs = cfg.paid && s.paid_dir ? ["--paid-dir", resolve(cwd, s.paid_dir)] : [];
364
285
  try {
365
- if (cfg.private) await publishSealed(cfg, base, s, { secret: secret(), dry });
366
- else run(s.command[0], [...s.command.slice(1), ...paidArgs, ...(replace ? ["--replace"] : []),
367
- "--publish", "--namespace", s.namespace, "--fangorn-bin", CLI.join(" ")],
368
- { cwd, env: { FANGORN_APP_ID: cfg.app }, dry });
286
+ run(s.command[0], [...s.command.slice(1), ...(replace ? ["--replace"] : []),
287
+ "--publish", "--namespace", s.namespace, "--fangorn-bin", CLI.join(" ")], { cwd, dry });
369
288
  } catch (e) {
370
289
  log(`✗ ${s.namespace}: ${e.message}; its last published records stay live`);
371
290
  failed.push(s.namespace);
@@ -378,26 +297,7 @@ export async function ship(configPath, { crawl = true, deploy = true, dry = fals
378
297
  // which is only known once it is deployed: the first deploy learns it and the card follows.
379
298
  let url = cfg.site.url?.replace(/\/$/, "") ?? state.url ?? null;
380
299
  const repos = presentedRepos(cfg);
381
- // Paid records are in the committed rows already, encrypted (`paid_ct`: AES-256-GCM under
382
- // HMAC-SHA256(PAID_SECRET, id), see x402.js). The Worker sells keys, never records, so the
383
- // data a buyer pays for is on IPFS, committed, and in their hands before they pay.
384
- const terms = (o) => settlement(o, { network: `eip155:${FangornConfig.caip2}`, owner });
385
- const sale = (u) => {
386
- if (!cfg.paid) return { paid: null, sale: null };
387
- const T = terms(cfg.paid), price = units(cfg.paid.price, T.decimals), description = cfg.paid.description ?? "";
388
- const paid = { url: `${u}/paid/{id}`, price, asset: T.asset, network: T.network, decimals: T.decimals, symbol: T.symbol, description };
389
- return { paid, sale: { ...T, price, description } };
390
- };
391
- const access = (u) => {
392
- const X = cfg.private;
393
- if (!X) return { card: null, config: null };
394
- if (!X.pass?.price || !X.call) throw new Error(`${configPath}: private needs pass.price and call, decimal prices like "5" and "0.01"`);
395
- const T = terms(X), days = X.pass.days ?? 30, description = X.description ?? "";
396
- const pass = { price: units(X.pass.price, T.decimals), seconds: Math.round(days * 86400) }, call = units(X.call, T.decimals);
397
- return { card: { pass_url: `${u}/pass`, pass: pass.price, days, call, network: T.network, asset: T.asset, symbol: T.symbol, decimals: T.decimals, description },
398
- config: { ...T, pass, call, description } };
399
- };
400
- const writeSite = (u) => writeCard(u, { fromBlock: state.fromBlock, repos, paid: sale(u).paid, access: access(u).card });
300
+ const writeSite = (u) => writeCard(u, { owner: me, fromBlock: state.fromBlock, repos, paid: !!cfg.paid });
401
301
  writeSite(url ?? `https://${cfg.site.project}.workers.dev`);
402
302
 
403
303
  // 4. the Worker
@@ -406,7 +306,8 @@ export async function ship(configPath, { crawl = true, deploy = true, dry = fals
406
306
  writeFileSync(path, JSON.stringify(wranglerConfig({
407
307
  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(),
408
308
  config: { app: cfg.app, name: cfg.name, description: cfg.description, fromBlock: state.fromBlock, network: cfg.network,
409
- schema: { types: cfg.types, relations: cfg.relations }, repos: register ? [] : repos, paid: sale(u).sale, private: access(u).config },
309
+ schema: { types: cfg.types, relations: cfg.relations }, repos: register ? [] : repos, paid: !!cfg.paid,
310
+ ...(cfg.facilitator ? { facilitator: cfg.facilitator } : {}) },
410
311
  }), null, 1));
411
312
  return path;
412
313
  };
@@ -426,22 +327,20 @@ export async function ship(configPath, { crawl = true, deploy = true, dry = fals
426
327
  }
427
328
  state.url = url;
428
329
  save();
429
- if (cfg.private) run("npx", ["wrangler", "secret", "put", "PAID_SECRET", "--config", workerConfig(url)], { env: cf, capture: true, dry, input: secret() });
430
- else if (cfg.paid) log("paid: set the key secret once with `npx wrangler secret put PAID_SECRET --config .ship/wrangler.json`");
431
330
  } else workerConfig(url ?? `https://${cfg.site.project}.workers.dev`);
432
331
 
433
- // 5. ERC-8004 + the binding
332
+ // 5. ERC-8004 + the repo's @card
434
333
  const cardUrl = `${url}/.well-known/agent-card.json`;
435
- const bound = await registry.appAgentUri(appId).catch(() => "");
334
+ const bound = (await fangorn.cardOf(me, cfg.app).catch(() => undefined)) ?? "";
436
335
  const mint = () => {
437
- const out = dry ? "" : fangornCli(cfg.app, ["app", "agent", cardUrl]);
336
+ const out = dry ? "" : fangornCli(["card", cardUrl, "--repo", cfg.app]);
438
337
  state.agentId = (out.match(/Agent ID:\s+(\S+)/) ?? [])[1] ?? state.agentId;
439
338
  delete state.agentPending;
440
339
  save();
441
340
  };
442
341
  if (deploy && bound !== cardUrl && register) {
443
342
  log(`binding ${cardUrl} (on chain: ${bound || "nothing"}); the agent is minted on the first full ship`);
444
- fangornCli(cfg.app, ["app", "agent", cardUrl, "--skip-register"], { dry });
343
+ fangornCli(["card", cardUrl, "--repo", cfg.app, "--skip-register"], { dry });
445
344
  state.agentPending = true;
446
345
  save();
447
346
  } else if (deploy && bound !== cardUrl) {
@@ -458,93 +357,11 @@ export async function ship(configPath, { crawl = true, deploy = true, dry = fals
458
357
  await indexHere(configPath).catch((e) => log(`✗ first index: ${e.message}; the cron will build it`));
459
358
  }
460
359
 
461
- // The Bazaar: the facilitator lists POST /paid once it settles a sale (and decides to);
462
- // nothing here can list it sooner, so ship only says where it stands.
463
- if (deploy && (cfg.paid || cfg.private) && !dry) for (const [route, T] of [["paid", sale(url).sale], ["pass", access(url).config]]) {
464
- if (!T) continue;
465
- const listed = await fetch(`${T.facilitator}/discovery/resources?payTo=${T.payTo}`).then((r) => r.json())
466
- .then((d) => (d.items ?? []).some((i) => i.resource === `${url}/${route}`), () => null);
467
- log(listed ? `the Bazaar at ${T.facilitator} lists ${url}/${route}`
468
- : listed === false ? `the Bazaar at ${T.facilitator} does not list ${url}/${route} yet: it is listed on its first sale`
469
- : `could not read the Bazaar at ${T.facilitator}`);
470
- }
471
-
472
360
  log(`done: ${url} · card ${cardUrl} · MCP ${url}/mcp · the view fills on the Worker's next cron run`);
473
361
  if (failed.length) throw new Error(`deployed, but these sources failed and kept their old records: ${failed.join(", ")}`);
474
362
  return { url, cardUrl, agentId: state.agentId };
475
363
  }
476
364
 
477
- /** A private app's source: staged (it needs an --output-dir), every row sealed whole, and committed
478
- * as a full snapshot. Unchanged rows seal to the same bytes, so the Worker embeds only what changed. */
479
- async function publishSealed(cfg, base, s, { secret, dry }) {
480
- const { sealRow } = await import("../agent/x402.js");
481
- const rows = readStage({ ...cfg, sources: [s] }, base, { crawl: true })[s.namespace];
482
- const sdir = join(base, ".ship/sealed", s.namespace);
483
- mkdirSync(sdir, { recursive: true });
484
- const records = await Promise.all(rows.map(async (r) => ({ id: r.id, type: r.type, payload: await sealRow(secret, r.payload) })));
485
- writeFileSync(join(sdir, "commit.json"), JSON.stringify({ records }));
486
- log(`${s.namespace}: ${records.length} rows sealed`);
487
- try { fangornCli(cfg.app, ["status"], { cwd: sdir, dry }); } catch { fangornCli(cfg.app, ["repo", "init", s.namespace], { cwd: sdir, dry }); }
488
- fangornCli(cfg.app, ["commit", "commit.json", "-m", `${s.namespace}: ${records.length} rows, sealed`, "--replace"], { cwd: sdir, dry });
489
- fangornCli(cfg.app, ["push"], { cwd: sdir, dry });
490
- }
491
-
492
- /** What x402scan reads (docs/DISCOVERY.md in Merit-Systems/x402scan): /openapi.json with
493
- * `x-payment-info` on each paid route, and /.well-known/x402 naming them. POST /paid (keys to
494
- * sealed records) and POST /pass (a private app's pass); not GET /paid/{id}, a template no probe
495
- * can call. The worker's live 402 is authoritative over both. A private app's tools, paid per
496
- * call over MCP, are listed in the Bazaar instead: x402scan reads HTTP routes only.
497
- * ponytail: currency "USD" assumes the asset is USDC, the only default ship knows. */
498
- export function discovery(cfg, u, paid = null, access = null) {
499
- const token = paid ?? access;
500
- const usd = (n) => String(Number(n) / 10 ** (token.decimals ?? 6));
501
- const ids = { type: "array", items: { type: "string" }, minItems: 1, maxItems: 1000 };
502
- const sym = token.symbol ?? "USDC";
503
- const paths = {};
504
- if (paid) paths["/paid"] = { post: {
505
- operationId: "buyKeys",
506
- summary: `Keys to ${cfg.name}'s sealed records, ${usd(paid.price)} ${sym} each`,
507
- description: `${paid.description ? `${paid.description}. ` : ""}Each record is in the app's rows already, sealed (paid_ct, `
508
- + `AES-256-GCM, with paid_sha256 of the plaintext): this sells its key. Find records and their ids with the MCP server at ${u}/mcp; `
509
- + `the agent card is ${u}/.well-known/agent-card.json.`,
510
- requestBody: { required: true, content: { "application/json": { schema: { type: "object", required: ["ids"], properties: { ids } } } } },
511
- responses: {
512
- 200: { description: "A hex key per id; ids it could not sell are in missing", content: { "application/json": { schema: { type: "object", properties: {
513
- keys: { type: "object", additionalProperties: { type: "string" } }, missing: ids, receipts: { type: "array", items: { type: "object" } },
514
- paid: { type: "array", items: { type: "string" } } } } } } },
515
- 402: { description: "Payment required: x402 v2, the PaymentRequired in the PAYMENT-REQUIRED header" },
516
- },
517
- "x-payment-info": { protocols: [{ x402: {} }], price: { mode: "dynamic", currency: "USD", min: usd(paid.price), max: usd(BigInt(paid.price) * 1000n) } },
518
- } };
519
- if (access) paths["/pass"] = { post: {
520
- operationId: "buyPass",
521
- summary: `A ${access.days}-day pass to ${cfg.name}, ${usd(access.pass)} ${sym}`,
522
- description: `${access.description ? `${access.description}. ` : ""}Every tool of the MCP server at ${u}/mcp (search, get, similar, count, `
523
- + `browse) and the view at ${u}/view, for ${access.days} days: send Authorization: Bearer <token>. Without a pass each tool call costs `
524
- + `${usd(access.call)} ${sym} over x402's MCP transport.`,
525
- responses: {
526
- 200: { description: "The pass", content: { "application/json": { schema: { type: "object", properties: {
527
- token: { type: "string" }, expires: { type: "integer", description: "Unix seconds" }, use: { type: "string" } } } } } },
528
- 402: { description: "Payment required: x402 v2, the PaymentRequired in the PAYMENT-REQUIRED header" },
529
- },
530
- "x-payment-info": { protocols: [{ x402: {} }], price: { mode: "fixed", currency: "USD", amount: usd(access.pass) } },
531
- } };
532
- const guidance = access
533
- ? `${cfg.name}'s data is private. describe on the MCP server at ${u}/mcp is free. Buy a pass (POST /pass, x402 v2) and send `
534
- + `Authorization: Bearer <token> to ${u}/mcp, or pay each tool call over x402's MCP transport (_meta["x402/payment"]).`
535
- : `Search ${cfg.name} with the MCP server at ${u}/mcp (free). A row with paid_ct is for sale: POST its id to /paid, `
536
- + `pay the 402 (x402 v2), and open paid_ct with the key that comes back (AES-256-GCM, nonce first; check paid_sha256).`;
537
- return {
538
- openapi: {
539
- openapi: "3.1.0",
540
- info: { title: cfg.name, version: new Date().toISOString().slice(0, 10), description: cfg.description, "x-guidance": guidance },
541
- servers: [{ url: u }],
542
- paths,
543
- },
544
- x402: { version: 1, resources: Object.keys(paths).map((p) => `${u}${p}`) },
545
- };
546
- }
547
-
548
365
  /** The stock page with the app's own files over it, in .ship/site; `writeCard(url, …)` adds the card for where it is served. */
549
366
  export async function buildSite(cfg, base) {
550
367
  if (!existsSync(join(STOCK, "index.html"))) throw new Error(`no stock page at ${STOCK}; run \`npx vite build\` in westmarch/site`);
@@ -569,27 +386,18 @@ export async function buildSite(cfg, base) {
569
386
  const { registerAgent } = await import(cfg.site.agent ? pathToFileURL(resolve(base, cfg.site.agent)).href : "../../site/agent.js");
570
387
  const tools = await card.captureTools(() => registerAgent({}));
571
388
  const config = await (await import("./view.js")).network(cfg.network);
572
- const writeCard = (u, { fromBlock, repos, paid = null, access = null }) => {
389
+ const writeCard = (u, { owner, fromBlock, repos, paid = false }) => {
573
390
  const agentCard = card.agentCard({
574
391
  name: cfg.name, description: cfg.description, url: `${u}/`, version: new Date().toISOString().slice(0, 10), tools, tags: cfg.tags,
575
392
  // What is sold, as a skill tagged x402: ERC-8004 registration reads x402Support off the tags.
576
- skills: [
577
- ...(access ? [{ id: "pass", name: "Pass", tags: [...(cfg.tags ?? []), "x402"],
578
- description: `A ${access.days}-day pass to every tool and the view (POST ${u}/pass), or one x402 payment per tool call.` }] : []),
579
- ...(paid ? [{ id: "keys", name: "Keys", tags: [...(cfg.tags ?? []), "x402"],
580
- description: `The keys to sealed records (POST ${u}/paid), per record.` }] : []),
581
- ],
582
- fangorn: { app: cfg.app, fromBlock, repos, views: [`${u}/view`], mcp: `${u}/mcp`, config, ...(paid ? { paid } : {}), ...(access ? { access } : {}),
393
+ skills: paid ? [{ id: "records", name: "Paid records", tags: [...(cfg.tags ?? []), "x402"],
394
+ description: `Records whose publishers sell fields (rows with a \`sale\`): GET ${u}/paid/{publisher}/{repo}/{id} quotes one over x402 (scheme "fangorn").` }] : [],
395
+ fangorn: { app: cfg.app, owner, fromBlock, repos, views: [`${u}/view`], mcp: `${u}/mcp`, config, ...(paid ? { paid: { url: `${u}/paid/{publisher}/{repo}/{id}` } } : {}),
583
396
  ...(skill ? { skill: { url: `${u}/${skill.path}`, sha256: skill.sha256 } } : {}),
584
397
  ...(cfg.site.plugin ? { plugin: cfg.site.plugin } : {}) },
585
398
  });
586
399
  mkdirSync(join(site, ".well-known"), { recursive: true });
587
400
  writeFileSync(join(site, ".well-known/agent-card.json"), JSON.stringify(agentCard, null, 1));
588
- if (paid || access) {
589
- const d = discovery(cfg, u, paid, access);
590
- writeFileSync(join(site, "openapi.json"), JSON.stringify(d.openapi, null, 1));
591
- writeFileSync(join(site, ".well-known/x402"), JSON.stringify(d.x402, null, 1));
592
- }
593
401
  writeFileSync(join(site, "_headers"), "/.well-known/agent-card.json\n Access-Control-Allow-Origin: *\n"
594
402
  + "/openapi.json\n Access-Control-Allow-Origin: *\n"
595
403
  + "/.well-known/x402\n Access-Control-Allow-Origin: *\n Content-Type: application/json\n"
@@ -640,7 +448,7 @@ export function readStage(cfg, base, { crawl = false, only = null, log: say = lo
640
448
  }
641
449
 
642
450
  /**
643
- * The whole app on this machine (devnet.js): a local chain and IPFS, the app claimed there, what the sources staged published into it, and its Worker under
451
+ * The whole app on this machine (devnet.js): a local chain and IPFS, what the sources staged published there, and its Worker under
644
452
  * `wrangler dev` with a local R2 and an encoder on localhost (or Workers AI with ai=true).
645
453
  * Runs the indexer once, says where everything is, and stays up until interrupted.
646
454
  */
@@ -650,9 +458,11 @@ export async function devnetShip(configPath, { crawl = false, only = null, port
650
458
  const { devnet } = await import("./devnet.js");
651
459
  const staged = readStage(cfg, base, { crawl, only });
652
460
  const net = await devnet({ log });
653
- const owner = await net.client(cfg.app);
654
- await owner.getAppRegistry().registerApp();
655
- // The claimer publishes the sources, as `fangorn` does in a ship.
461
+ // A paid app's services, as a deployment runs them: the key server publishers deposit with,
462
+ // and Barliman, which its quotes settle through.
463
+ const services = cfg.paid ? await Promise.all([net.keyServer(), net.relay()]).then(([keys, r]) => ({ keys, relay: r.url })) : null;
464
+ const owner = await net.client();
465
+ // The owner publishes the sources, as `fangorn` does in a ship.
656
466
  for (const [ns, records] of Object.entries(staged)) {
657
467
  if (!records.length) continue;
658
468
  await owner.initRepo(ns);
@@ -663,9 +473,10 @@ export async function devnetShip(configPath, { crawl = false, only = null, port
663
473
  const url = `http://127.0.0.1:${port}`;
664
474
  const repos = Object.keys(staged);
665
475
  const { site, writeCard } = await buildSite(cfg, base);
666
- writeCard(url, { fromBlock: "0", repos });
476
+ writeCard(url, { owner: owner.getAddress(), fromBlock: "0", repos, paid: !!services });
667
477
  const wcfg = wranglerConfig({ name: cfg.site.project, site, config: { app: cfg.app, name: cfg.name, description: cfg.description,
668
- fromBlock: "0", schema: { types: cfg.types, relations: cfg.relations }, repos, paid: null, deployment: net.deployment } });
478
+ fromBlock: "0", schema: { types: cfg.types, relations: cfg.relations }, repos, paid: !!services, deployment: net.deployment,
479
+ ...(services ? { facilitator: services.relay } : {}) } });
669
480
  // Its own R2 and state, apart from a deployed app's.
670
481
  wcfg.vars.RPC_URL = net.rpcUrl;
671
482
  wcfg.vars.IPFS_GATEWAY = net.gatewayUrl;
@@ -693,13 +504,15 @@ export async function devnetShip(configPath, { crawl = false, only = null, port
693
504
  log(`done: ${catalog?.domains?.length ?? 0} domains, ${(catalog?.domains ?? []).reduce((n, d) => n + d.count, 0)} rows`);
694
505
  log(`site ${url} · MCP ${url}/mcp · view ${url}/view · reindex: curl '${url}/__scheduled' · Ctrl-C stops it all`);
695
506
  log(`grade it: npx westmarch-eval ${url}/view --golden eval/golden.jsonl`);
507
+ // ponytail: the devnet's IPFS is in this process's memory, so records are sold from here (sealRecords),
508
+ // not from a separate `fangorn` CLI; scripts/e2e-paid.mjs does exactly that.
509
+ if (services) log(`paid: key server ${services.keys} · Barliman ${services.relay}`);
696
510
  await new Promise((r) => wrangler.on("exit", r));
697
511
  }
698
512
 
699
513
  /**
700
514
  * The deployed app's indexer, run once on this machine: its Worker under `wrangler dev` with
701
- * the app's own R2 bucket and Workers AI (remote bindings), its CONFIG, and for a private app
702
- * its PAID_SECRET (in .ship/.dev.vars for the run, then removed). The first view right after a
515
+ * the app's own R2 bucket and Workers AI (remote bindings) and its CONFIG. The first view right after a
703
516
  * deploy, without waiting for the cron; and a catalog too big for one cron run's limits, since
704
517
  * nothing here is capped. Writes the same view the cron would.
705
518
  * ponytail: Workers AI still embeds; an encoder on this machine (EMBED_URL) would record another
@@ -713,11 +526,8 @@ export async function indexHere(configPath, { port = 8791 } = {}) {
713
526
  w.r2_buckets = w.r2_buckets.map((b) => ({ ...b, remote: true }));
714
527
  w.ai = { ...w.ai, remote: true };
715
528
  delete w.routes;
716
- const wpath = join(dir, "wrangler.index.json"), vars = join(dir, ".dev.vars");
529
+ const wpath = join(dir, "wrangler.index.json");
717
530
  writeFileSync(wpath, JSON.stringify(w, null, 1));
718
- const secret = process.env.PAID_SECRET ?? (existsSync(join(dir, "secret")) ? readFileSync(join(dir, "secret"), "utf8").trim() : null);
719
- if (cfg.private && !secret) throw new Error("a private app's rows open only with its PAID_SECRET: none in $PAID_SECRET or .ship/secret");
720
- if (secret) writeFileSync(vars, `PAID_SECRET=${secret}\n`, { mode: 0o600 });
721
531
  const url = `http://127.0.0.1:${port}`, start = Math.floor(Date.now() / 1000);
722
532
  const wrangler = spawn("npx", ["wrangler", "dev", "--config", wpath, "--port", String(port), "--ip", "127.0.0.1", "--test-scheduled"],
723
533
  { cwd: join(HERE, "../.."), stdio: ["ignore", "pipe", "inherit"],
@@ -744,7 +554,6 @@ export async function indexHere(configPath, { port = 8791 } = {}) {
744
554
  return catalog;
745
555
  } finally {
746
556
  wrangler.kill();
747
- rmSync(vars, { force: true });
748
557
  }
749
558
  }
750
559
 
@@ -761,8 +570,8 @@ export async function buildLocal(configPath, out, { crawl = false, only = null,
761
570
  const base = dirname(resolve(configPath));
762
571
  const [{ env }, view] = await Promise.all([import("@huggingface/transformers"), import("./view.js")]);
763
572
  env.cacheDir = `${homedir()}/.cache/fangorn-mcp/models`;
764
- // Not the app's real id or owner: nothing here is compared with the chain.
765
- const appId = `0x${createHash("sha256").update(cfg.app).digest("hex")}`, owner = `0x${"10ca1".padStart(40, "0")}`;
573
+ // Not the app's real owner: nothing here is compared with the chain.
574
+ const owner = `0x${"10ca1".padStart(40, "0")}`;
766
575
  const chain = {};
767
576
  // Storage addresses a record by its content, and so does this.
768
577
  if (cfg.sources.length) for (const [ns, records] of Object.entries(readStage(cfg, base, { crawl, only, log: say })))
@@ -772,11 +581,10 @@ export async function buildLocal(configPath, out, { crawl = false, only = null,
772
581
  if (cfg.live) {
773
582
  if (cfg.network) process.env.FANGORN_NETWORK = process.env.CHAIN_NAME = cfg.network;
774
583
  const fangorn = await view.readOnlyFangorn();
775
- fangorn.setAppId(cfg.app);
776
584
  const statePath = join(base, ".ship/state.json");
777
585
  const fromBlock = (existsSync(statePath) ? JSON.parse(readFileSync(statePath, "utf8")).fromBlock : null) ?? cfg.fromBlock;
778
586
  const staged = new Set(cfg.sources.map((s) => s.namespace));
779
- for (const t of await fangorn.appRepos(fromBlock == null ? {} : { fromBlock: BigInt(fromBlock) })) {
587
+ for (const t of await fangorn.repos(fromBlock == null ? {} : { fromBlock: BigInt(fromBlock) })) {
780
588
  if (t.repo === "fangorn.schema" || staged.has(t.repo) || (only && !only.includes(t.repo))
781
589
  || (cfg.live !== true && !cfg.live.includes(t.repo))) continue;
782
590
  const { contents } = await fangorn.readRepo(t.owner, t.repo);
@@ -784,12 +592,12 @@ export async function buildLocal(configPath, out, { crawl = false, only = null,
784
592
  say(`live ${t.owner}/${t.repo}: ${contents.records.length} records, from the chain`);
785
593
  }
786
594
  }
787
- if (!Object.keys(chain).length) throw new Error("nothing to build: no sources staged anything and no member has pushed to a live namespace");
595
+ if (!Object.keys(chain).length) throw new Error("nothing to build: no sources staged anything and no publisher has pushed to a live namespace");
788
596
  rmSync(out, { recursive: true, force: true });
789
597
  mkdirSync(join(base, ".ship"), { recursive: true });
790
598
  const embed = view.cachedEmbed(join(base, ".ship/vectors.ndjson"));
791
599
  const repos = presentedRepos(cfg).filter((n) => !only || only.includes(n));
792
- const report = await view.publishView({ fangorn: view.localFangorn(chain, { appId, owner }), app: cfg.app,
600
+ const report = await view.publishView({ fangorn: view.localFangorn(chain), app: cfg.app,
793
601
  schema: { types: cfg.types, relations: cfg.relations }, repos, out, embed, log: say });
794
602
  const rows = Object.values(report).reduce((n, r) => n + r.added, 0);
795
603
  say(`local view: ${out} · ${rows} records, ${embed.misses} embedded, ${rows - embed.misses} from the vector cache`);
@@ -807,26 +615,17 @@ if (process.argv[1]?.endsWith("ship.js") || process.argv[1]?.endsWith("westmarch
807
615
  if (w.r2_buckets[0].bucket_name !== "quorum-view" || !w.ai || !w.triggers.crons.length || w.routes[0].pattern !== "quorum.example"
808
616
  || !w.compatibility_flags.includes("nodejs_compat") || !existsSync(w.main) || !existsSync(w.alias["@huggingface/transformers"]))
809
617
  throw new Error(`wrangler config: ${JSON.stringify(w)}`);
810
- const d = discovery({ name: "Q", description: "q" }, "https://q.example", { price: "10000", decimals: 6, symbol: "USDC" });
811
- const op = d.openapi.paths["/paid"].post;
812
- if (op["x-payment-info"].price.min !== "0.01" || op["x-payment-info"].price.max !== "10" || !op.responses[402] || d.x402.resources[0] !== "https://q.example/paid")
813
- throw new Error(`discovery: ${JSON.stringify(d)}`);
814
- const dp = discovery({ name: "Q", description: "q" }, "https://q.example", null, { pass: "5000000", days: 30, call: "10000", decimals: 6 });
815
- if (dp.openapi.paths["/pass"].post["x-payment-info"].price.amount !== "5" || dp.x402.resources.join() !== "https://q.example/pass" || dp.openapi.paths["/paid"])
816
- throw new Error(`private discovery: ${JSON.stringify(dp.x402)}`);
817
618
  const src = [{ namespace: "towns", command: ["py"] }];
818
619
  if (presentedRepos({ sources: src }).join() !== "towns" || presentedRepos({ sources: src, live: ["readings", "towns"] }).join() !== "towns,readings"
819
620
  || presentedRepos({ sources: src, live: true }).length || presentedRepos({ sources: [] }).length)
820
621
  throw new Error("presentedRepos: sources plus live namespaces; [] (every repo) for live: true or no sources");
821
- if (units("0.01") !== "10000" || units("5") !== "5000000" || units("1.5", 2) !== "150") throw new Error("units");
822
- if (USDC["eip155:84532"].name !== "USDC" || USDC["eip155:8453"].name !== "USD Coin") throw new Error("USDC's EIP-712 name differs on Base Sepolia and Base");
823
- console.log("ship.js self-check ok — a source template fills per row, the Worker config binds its bucket, AI and cron, and the paid route is priced per id in /openapi.json and named in /.well-known/x402");
622
+ console.log("ship.js self-check ok — a source template fills per row, the Worker config binds its bucket, AI and cron, and presentedRepos merges sources and live namespaces");
824
623
  process.exit(0);
825
624
  }
826
625
  const [path, ...flags] = process.argv.slice(2);
827
626
  if (!path || flags.includes("--help")) {
828
627
  console.error("usage: westmarch-ship app.json [--no-crawl] [--no-deploy] [--dry-run] [--replace] [--only ns1,ns2]\n" +
829
- " westmarch-ship app.json --register [--dry-run] (claim, empty site, card bound: no data)\n" +
628
+ " westmarch-ship app.json --register [--dry-run] (the app's repo, empty site, card bound: no data)\n" +
830
629
  " westmarch-ship app.json --local <out> [--crawl] [--only ns1,ns2] (no chain, no deploy)\n" +
831
630
  " westmarch-ship app.json --index (the deployed app's indexer, once, on this machine)\n" +
832
631
  " westmarch-ship app.json --devnet [--crawl] [--only ns1,ns2] [--port 8787] [--ai] (the whole app on this machine)");