@fangorn-network/westmarch 0.0.20-dev → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. package/README.md +35 -5
  2. package/docs/app-to-agent.md +113 -620
  3. package/package.json +18 -18
  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-CkP1WhkV.js +8 -0
  7. package/site/dist/assets/roles-BDPDWrwo.js +1 -0
  8. package/site/dist/assets/rolldown-runtime-COiEfhsA.js +1 -0
  9. package/site/dist/assets/transformers.web-CUZUFjAK.js +49 -0
  10. package/site/dist/assets/transformers.web-CwR_IYQV.js +40 -0
  11. package/site/dist/index.html +4 -5
  12. package/site/feed.js +16 -1
  13. package/site/no-model.js +4 -0
  14. package/site/worker.js +382 -59
  15. package/src/agent/agent-card.js +51 -37
  16. package/src/agent/mcp.js +173 -500
  17. package/src/agent/tools.js +3 -2
  18. package/src/agent/ui.js +1 -1
  19. package/src/agent/view-tools.js +11 -30
  20. package/src/agent/x402.js +194 -85
  21. package/src/core/embed.js +56 -53
  22. package/src/core/embed.worker.js +1 -1
  23. package/src/core/rank.js +1 -1
  24. package/src/core/roles.js +7 -7
  25. package/src/core/shard.js +12 -8
  26. package/src/discover/apps.js +103 -67
  27. package/src/discover/corpora.js +1 -1
  28. package/src/discover/directory.js +3 -3
  29. package/src/market/cohort.js +1 -1
  30. package/src/market/demand.js +1 -1
  31. package/src/market/reactions.js +24 -18
  32. package/src/publish/devnet.js +146 -0
  33. package/src/publish/graph.js +1 -1
  34. package/src/publish/init.js +74 -0
  35. package/src/publish/lint.js +3 -2
  36. package/src/publish/ship.js +433 -287
  37. package/src/publish/view-cli.js +14 -10
  38. package/src/publish/view.js +485 -255
  39. package/src/taste/steam.js +1 -1
  40. package/src/taste/taste-doc.js +4 -3
  41. package/src/taste/taste.js +3 -3
  42. package/src/taste/vault.js +4 -2
  43. package/template/README.md +48 -0
  44. package/template/app.json +27 -0
  45. package/template/eval/goals.md +7 -0
  46. package/template/eval/golden.jsonl +0 -0
  47. package/template/gitignore +3 -0
  48. package/template/package.json +13 -0
  49. package/template/wrangler.json +12 -0
  50. package/plugin/scripts/fx.mjs +0 -268
  51. package/site/dist/assets/AccountNotFoundScreen-C4njXc69-BOfmiSH1.js +0 -1
  52. package/site/dist/assets/AddFundsSelectionScreen-RAoCn7ej-D_EsIeJu.js +0 -31
  53. package/site/dist/assets/Address-H8kD1d9O-C85sJ79B.js +0 -17
  54. package/site/dist/assets/AffirmativeConsentScreen-BG0MJxku-DdEPFOww.js +0 -6
  55. package/site/dist/assets/AllowlistRejectionScreen-Cae6e1Od-DhVy9iGN.js +0 -1
  56. package/site/dist/assets/ApiController-CYdiyu9B.js +0 -5
  57. package/site/dist/assets/ArrowsRightLeftIcon-cp60yobq.js +0 -1
  58. package/site/dist/assets/AuthenticateWithWalletScreen-CU6hIcFT-8rbYiYYE.js +0 -1
  59. package/site/dist/assets/AwaitingEvmToSolBridgingScreen-D0e_sZMS-2pkNfCno.js +0 -1
  60. package/site/dist/assets/AwaitingExternalEthereumTransferScreen-ur5HL4R7-7AQt25_k.js +0 -1
  61. package/site/dist/assets/AwaitingPasswordlessCodeScreen-DRHz76mM-BZY30TGs.js +0 -103
  62. package/site/dist/assets/AwaitingSolToEvmBridgingScreen-CdLRrGD2-BILOOLi_.js +0 -1
  63. package/site/dist/assets/BridgeNetworkSelectionView-CFGjB2nO-CvsbQcFv.js +0 -68
  64. package/site/dist/assets/CaptchaScreen-DbpvkIGx-D8eDcAkc.js +0 -1
  65. package/site/dist/assets/CheckCircleIcon-BM409mGI.js +0 -1
  66. package/site/dist/assets/CheckCircleIcon-NCOQc0Gs.js +0 -1
  67. package/site/dist/assets/Checkbox-D1EDeo41-rHlvsZzP.js +0 -52
  68. package/site/dist/assets/ChevronDownIcon-CW-4MSpE.js +0 -1
  69. package/site/dist/assets/Chip-CZKIKt9K-CutJG010.js +0 -26
  70. package/site/dist/assets/CoinbaseOnrampStatusScreen-BbyCJDXL-DLs9zX-Q.js +0 -16
  71. package/site/dist/assets/ConnectEmailForm-B-VRE0MN-N9J6T_YL.js +0 -3
  72. package/site/dist/assets/ConnectLedgerScreen-DWHABGot-h0uYIUi6.js +0 -10
  73. package/site/dist/assets/ConnectOnlyLandingScreen-CZYx9Bmp-LeuDMN1E.js +0 -1
  74. package/site/dist/assets/ConnectOnlyStatusScreen-DCRzjxHx-DaRa59tZ.js +0 -1
  75. package/site/dist/assets/ConnectOrCreateScreen-CUUYUO07-crHzh-TL.js +0 -1
  76. package/site/dist/assets/ConnectPhoneForm-Dek5xn4C-BEA4FmhI.js +0 -95
  77. package/site/dist/assets/ConnectWalletView-DqI-PTvX-CMQIad63.js +0 -74
  78. package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-BeWLchcf.js +0 -7
  79. package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-wu3a3kw4.js +0 -1
  80. package/site/dist/assets/ConnectorUtil-Cq4lWnfe.js +0 -21
  81. package/site/dist/assets/ConstantsUtil-DmLsRHAl.js +0 -198
  82. package/site/dist/assets/CopyToClipboard-i_OQSBJr-DujUHNzW.js +0 -39
  83. package/site/dist/assets/CopyableText-CQapvaMr-DMIdpDaU.js +0 -28
  84. package/site/dist/assets/CrossAppAuthScreen-CLw7ymZq-Csju14vF.js +0 -1
  85. package/site/dist/assets/CustomLandingScreenView-Cm_MBSyX-CBOrbNLo.js +0 -22
  86. package/site/dist/assets/DelegatedActionsConsentScreen-DYF2U4y5-BL5hzNUI.js +0 -1
  87. package/site/dist/assets/DelegatedActionsRevokeScreen-bPp6Uwyh-fcO51q5H.js +0 -1
  88. package/site/dist/assets/DepositAddressAddressStepView-4vdkb55L-B3vY888G.js +0 -187
  89. package/site/dist/assets/DepositAddressScreen-DBLRs7aI-DmdQaFI2.js +0 -8
  90. package/site/dist/assets/DepositAddressV3Screen-CDqIEMQw-BoupRyfV.js +0 -1
  91. package/site/dist/assets/EmailInputForm-Cqo1mda8-B2NG3K5V.js +0 -124
  92. package/site/dist/assets/EmbeddedWalletConnectingScreen-D4T7DUzs-D_AVgO4s.js +0 -1
  93. package/site/dist/assets/EmbeddedWalletCreatedScreen-D7WhW5ve-CLsJbPYQ.js +0 -1
  94. package/site/dist/assets/EmbeddedWalletKeyExportScreen-CqV4NYQ3-C9sAExzE.js +0 -23
  95. package/site/dist/assets/EmbeddedWalletOnAccountCreateScreen-BoKb3Q2S-DxaWAAKa.js +0 -1
  96. package/site/dist/assets/EmbeddedWalletPasswordUpdateScreen-D3rMlqS7-CxrfLr5q.js +0 -1
  97. package/site/dist/assets/EmbeddedWalletPasswordUpdateSplashScreen-BPFkWJeE-DHZRGoTN.js +0 -1
  98. package/site/dist/assets/EnrollTotp-Dibew4dp-SA2pvNEc.js +0 -66
  99. package/site/dist/assets/EnvelopeIcon-Dh2vSVj4.js +0 -1
  100. package/site/dist/assets/ErrorBanner-BcpGRt0h-BKOYTyCi.js +0 -21
  101. package/site/dist/assets/ErrorMessage-D8VaAP5m-CrZOnUyg.js +0 -8
  102. package/site/dist/assets/ErrorScreen-DipdTWzE-DcxVjMO4.js +0 -1
  103. package/site/dist/assets/ErrorScreen-DipdTWzE-c8g8En21.js +0 -4
  104. package/site/dist/assets/ExclamationCircleIcon-DUA2wMfO.js +0 -1
  105. package/site/dist/assets/ExclamationTriangleIcon-eZfaqcFq.js +0 -1
  106. package/site/dist/assets/FarcasterConnectStatusScreen-51tjUxqq-s_s5M2CZ.js +0 -75
  107. package/site/dist/assets/FarcasterSignerStatusScreen-6x-Uh95G-BHBR4k8H.js +0 -28
  108. package/site/dist/assets/FiatOnrampScreen-BAhYeiTI-BH_sU6EV.js +0 -491
  109. package/site/dist/assets/FingerPrintIcon-Dm6kxmg7.js +0 -1
  110. package/site/dist/assets/FundSolWalletWithExternalSolanaWallet-WtLwYaOl-B8TRV36j.js +0 -1
  111. package/site/dist/assets/FundWalletMethodHeader-BWNkokCT-BQdbJ5Bb.js +0 -1
  112. package/site/dist/assets/FundWithBankDepositScreen-DajkHxBD-9rPtyA3d.js +0 -58
  113. package/site/dist/assets/FundingEditAmountScreen-CpvpiO4M-GaXigGI9.js +0 -1
  114. package/site/dist/assets/FundingMethodSelectionScreen-Db7vhRlE-UsmtTqv4.js +0 -1
  115. package/site/dist/assets/GlobeAltIcon-vqrVV5ho.js +0 -1
  116. package/site/dist/assets/GooglePay-B53WnudL-BX5_WtCx.js +0 -1
  117. package/site/dist/assets/HCaptchaWrapper-CUi9LJsp-D-I8j7NO.js +0 -4
  118. package/site/dist/assets/InAppBrowserLoginNotPossible-BMR9br1T-BY3j50El.js +0 -1
  119. package/site/dist/assets/InfoBanner-Cb3p1z12-DLBl_Ywy.js +0 -22
  120. package/site/dist/assets/InjectedWalletIcon-DLcYOGDj-DLSV_NjP.js +0 -1
  121. package/site/dist/assets/InstallWalletScreen-DTA8dwLv-CxreyXoD.js +0 -1
  122. package/site/dist/assets/JsonTree-BHzNC-ic-DcNcbSoM.js +0 -34
  123. package/site/dist/assets/LabelXs-oqZNqbm_-iafrj25N.js +0 -6
  124. package/site/dist/assets/LandingScreen-CGWUoSUG-DK1EY-6v.js +0 -1
  125. package/site/dist/assets/Layouts-BMRfo5hw-CeCOyCLK.js +0 -77
  126. package/site/dist/assets/Link-BdDilT2T-BEDEdHuS.js +0 -40
  127. package/site/dist/assets/LinkConflictScreen-C3ZEPsgl-ClWoqvEL.js +0 -77
  128. package/site/dist/assets/LinkEmailScreen-dKo9frwc-C3v7glH0.js +0 -1
  129. package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-D72Gb0LW.js +0 -117
  130. package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-DYIl5Kr2.js +0 -1
  131. package/site/dist/assets/LinkPhoneScreen-oIUpfj0w-C0DuD14j.js +0 -1
  132. package/site/dist/assets/Loader-UAZqxsnw-Cy7hEE2f.js +0 -62
  133. package/site/dist/assets/LoadingSkeleton-BMsgO5PV-CiEyS5PZ.js +0 -17
  134. package/site/dist/assets/LockClosedIcon-BgY_5v_Z.js +0 -1
  135. package/site/dist/assets/LoginFailedScreen-CN0dJV6U-DhUMiJZJ.js +0 -1
  136. package/site/dist/assets/ManualTransferScreen-CHqcuqbs-tXiZDHmN.js +0 -1
  137. package/site/dist/assets/MfaAuthEnrollmentFlowScreen-Dj7gPTRC-CaW7xeCC.js +0 -1
  138. package/site/dist/assets/MfaAuthVerifyFlowScreen-DbFpyWMy-CeRqvx8k.js +0 -1
  139. package/site/dist/assets/MfaEnrollmentFlowScreen-DhLnGgnr-BkN1k4lh.js +0 -1
  140. package/site/dist/assets/MfaVerifyFlowScreen-C_XPjMRP-CXSqLVbE.js +0 -1
  141. package/site/dist/assets/ModalFooter-BCuEK_nz-c-khvG4Y.js +0 -403
  142. package/site/dist/assets/MoonpayStatusScreen-CzTUtokc-BRFN2eXr.js +0 -16
  143. package/site/dist/assets/OAuthStatusScreen-ufbOa-da-CeiGcSia.js +0 -1
  144. package/site/dist/assets/OpenLink-CUpJ1mOr-DfAvJRyK.js +0 -39
  145. package/site/dist/assets/PasskeySelectSignupOrLogin-xh2ScC_l-CIf5I7wU.js +0 -1
  146. package/site/dist/assets/PasskeyStatusScreen-Bla8MaOk-CLoL7QC-.js +0 -6
  147. package/site/dist/assets/PasswordRecoveryScreen-awGXZkQk-B9F67IYV.js +0 -20
  148. package/site/dist/assets/PhArrowCircleDown-D97G-BOA.js +0 -14
  149. package/site/dist/assets/PhArrowClockwise-DVgyFuez.js +0 -14
  150. package/site/dist/assets/PhArrowDown-87b1BizM.js +0 -14
  151. package/site/dist/assets/PhArrowLeft-Da0MfnbK.js +0 -14
  152. package/site/dist/assets/PhArrowRight-AdrIL4DW.js +0 -14
  153. package/site/dist/assets/PhArrowSquareOut-Bk2CMG3I.js +0 -14
  154. package/site/dist/assets/PhArrowUp-4sgyKPGx.js +0 -14
  155. package/site/dist/assets/PhArrowUpRight-BNghD2zq.js +0 -14
  156. package/site/dist/assets/PhArrowsClockwise-BXRn1GEl.js +0 -14
  157. package/site/dist/assets/PhArrowsDownUp-DM09Jo_3.js +0 -14
  158. package/site/dist/assets/PhArrowsLeftRight-MuKBLUoF.js +0 -14
  159. package/site/dist/assets/PhBank-fHbgCqqZ.js +0 -14
  160. package/site/dist/assets/PhBrowser-2-9m-AkM.js +0 -14
  161. package/site/dist/assets/PhCaretDown-BDNabFuL.js +0 -14
  162. package/site/dist/assets/PhCaretLeft-CI460R-A.js +0 -14
  163. package/site/dist/assets/PhCaretRight-D7ZS3Ls8.js +0 -14
  164. package/site/dist/assets/PhCaretUp-C-iIfyvO.js +0 -14
  165. package/site/dist/assets/PhCheck-DxQXn57r.js +0 -14
  166. package/site/dist/assets/PhCircleHalf-MJZeRBGF.js +0 -14
  167. package/site/dist/assets/PhClock-x3MS3Mwi.js +0 -14
  168. package/site/dist/assets/PhCompass-hYn3HhGN.js +0 -14
  169. package/site/dist/assets/PhCopy-CWNYcABI.js +0 -14
  170. package/site/dist/assets/PhCreditCard-Fy4pzwtt.js +0 -14
  171. package/site/dist/assets/PhCurrencyDollar-BgiaenCi.js +0 -14
  172. package/site/dist/assets/PhDesktop-CxrzCSv5.js +0 -14
  173. package/site/dist/assets/PhDeviceMobile-CsyA5Goi.js +0 -14
  174. package/site/dist/assets/PhDotsThree-D-k1BiYg.js +0 -14
  175. package/site/dist/assets/PhEnvelope-D8kzeNox.js +0 -14
  176. package/site/dist/assets/PhFunnelSimple-CfQs5DAW.js +0 -14
  177. package/site/dist/assets/PhGlobe-DvmSc9_A.js +0 -14
  178. package/site/dist/assets/PhIdentificationCard-CfkZm-5K.js +0 -14
  179. package/site/dist/assets/PhImage-Cf0J2ZfJ.js +0 -14
  180. package/site/dist/assets/PhInfo-Bwi51-2X.js +0 -14
  181. package/site/dist/assets/PhLightbulb-C4FZV7Es.js +0 -14
  182. package/site/dist/assets/PhMagnifyingGlass-C9u8jSCJ.js +0 -14
  183. package/site/dist/assets/PhPaperPlaneRight-zxQVvPLX.js +0 -14
  184. package/site/dist/assets/PhPlus-C4UoW-2n.js +0 -14
  185. package/site/dist/assets/PhPower-CFyWDmgD.js +0 -14
  186. package/site/dist/assets/PhPuzzlePiece-BjoNVfST.js +0 -14
  187. package/site/dist/assets/PhQrCode-CP3tFauY.js +0 -14
  188. package/site/dist/assets/PhQuestion-wOWJBd3G.js +0 -14
  189. package/site/dist/assets/PhQuestionMark-CV0k9C0Y.js +0 -14
  190. package/site/dist/assets/PhSealCheck-vIKzREuN.js +0 -14
  191. package/site/dist/assets/PhSignOut-CxELV-i0.js +0 -14
  192. package/site/dist/assets/PhSpinner-CGDmbzUd.js +0 -14
  193. package/site/dist/assets/PhTrash-C9PD0SAt.js +0 -14
  194. package/site/dist/assets/PhUser-0sWNXQeZ.js +0 -14
  195. package/site/dist/assets/PhVault-6JTskUcC.js +0 -14
  196. package/site/dist/assets/PhWarning-CoKs3gz6.js +0 -14
  197. package/site/dist/assets/PhWarningCircle-BXHw1fCt.js +0 -14
  198. package/site/dist/assets/PhX-DX0SFIqC.js +0 -14
  199. package/site/dist/assets/PhoneIcon-CvIiPwYQ.js +0 -1
  200. package/site/dist/assets/PinInput-DbZ0b1i1-CDXZBdQx.js +0 -247
  201. package/site/dist/assets/PrivyPluginContext-DA-yS5vO-CtQWBiV2.js +0 -1
  202. package/site/dist/assets/QrCode-DbnZwlkH-h6Bhoe5U.js +0 -22
  203. package/site/dist/assets/RecoveryOAuthStatusScreen-CsgtTI2x-D-A5P3PX.js +0 -1
  204. package/site/dist/assets/RecoveryPasswordCreateScreen-Be-qJ-LU-C8lTSeSr.js +0 -1
  205. package/site/dist/assets/RecoverySelectionScreen-D1iGtpmi-P5P3plkH.js +0 -7
  206. package/site/dist/assets/SIWXUtil-TUCxa7aA.js +0 -1
  207. package/site/dist/assets/Screen-D30meWsY-CCeVNuWs.js +0 -175
  208. package/site/dist/assets/ScreenHeader-CHmc4-Lu-DjyrH7IK.js +0 -31
  209. package/site/dist/assets/ScreenLayout-OwaQQcxH-CrQM6kvA.js +0 -1
  210. package/site/dist/assets/SelectSourceAsset-CWFog6gm-D7yTw87h.js +0 -140
  211. package/site/dist/assets/SetAutomaticRecoveryScreen-BO_OEqAZ-f0iDIGmw.js +0 -14
  212. package/site/dist/assets/SetWalletPasswordForm-DwCTVtAh-kq1zvA7d.js +0 -11
  213. package/site/dist/assets/ShieldCheckIcon-o6RHYev1.js +0 -1
  214. package/site/dist/assets/SignRequestScreen-BHKv32KR-DDpMYyJ1.js +0 -26
  215. package/site/dist/assets/SolanaWalletConnector-C7Lqk-Wz-DPwhuzcI.js +0 -1
  216. package/site/dist/assets/StackedContainer-B2vaEl56-eQCM2cmv.js +0 -24
  217. package/site/dist/assets/StandardSignAndSendTransactionScreen-CfhIXVtd-BJ-onCjv.js +0 -20
  218. package/site/dist/assets/Subtitle-CV-2yKE4-zxWw2m1Q.js +0 -13
  219. package/site/dist/assets/TelegramAuthScreen-UL9zLfO8-NWlMgNRX.js +0 -1
  220. package/site/dist/assets/Title-BnzYV3Is-B_JpTatT.js +0 -7
  221. package/site/dist/assets/TodoList-DnyULl18-C8Eyy28X.js +0 -36
  222. package/site/dist/assets/TransactionDetails-e9nesl30-C4MpIHR-.js +0 -109
  223. package/site/dist/assets/TransactionErrorView-BDBNqA_f-p1T9WVMx.js +0 -178
  224. package/site/dist/assets/TransferFromWalletScreen-Qvn1Yuno-88IQcRnY.js +0 -31
  225. package/site/dist/assets/TransferOrBridgeLoadingScreen-Dl1I_iIz-1fDAIxAf.js +0 -6
  226. package/site/dist/assets/TurnstileWrapper-B7sNddys-Jwy1tJtm.js +0 -1
  227. package/site/dist/assets/UpdateEmailScreen-DVOhPV0H-Jj2DQW8D.js +0 -1
  228. package/site/dist/assets/UpdatePhoneScreen-CXRswTNZ-DwYIUBqi.js +0 -1
  229. package/site/dist/assets/UserLimitReachedScreen-5we2DHn8-D7m70C4b.js +0 -1
  230. package/site/dist/assets/Value-DTgR824E-BRuIGiec.js +0 -27
  231. package/site/dist/assets/WalletCards-DH1rqayz-Bgp9Fsgs.js +0 -1
  232. package/site/dist/assets/WalletIcon-gKf0gTw6.js +0 -1
  233. package/site/dist/assets/WalletInfoCard-sq_2wgFg-DpNSAGte.js +0 -29
  234. package/site/dist/assets/WalletInterstitialScreen-Sh7LfsAh-DG-RuO-F.js +0 -1
  235. package/site/dist/assets/WalletLink-DK5LB5Ha-DHaL350n.js +0 -20
  236. package/site/dist/assets/WalletOverflowButton-bfCnp-9T-CUyIJFzt.js +0 -1
  237. package/site/dist/assets/WarningBanner-ZZqCEtZK-2eU4Qo74.js +0 -21
  238. package/site/dist/assets/__vite-browser-external-CitKSmYk.js +0 -1
  239. package/site/dist/assets/_esm-B26FZg5Y.js +0 -7
  240. package/site/dist/assets/_esm-BdeM6pxJ.js +0 -1
  241. package/site/dist/assets/accounts-D96UKWkC.js +0 -1
  242. package/site/dist/assets/accounts-DiZ-0VmN.js +0 -1
  243. package/site/dist/assets/analytics-mkkvFRju-BRBcadka.js +0 -1
  244. package/site/dist/assets/baseSepolia-DX-qGsik.js +0 -1
  245. package/site/dist/assets/basic-oIwwSyu4.js +0 -1575
  246. package/site/dist/assets/capitalizeFirstLetter-DmLYqXsO-DtOXx-2n.js +0 -1
  247. package/site/dist/assets/ccip-65sIfSPR.js +0 -1
  248. package/site/dist/assets/ccip-BZvRTL6g.js +0 -7
  249. package/site/dist/assets/ccip-DA_zG341.js +0 -1
  250. package/site/dist/assets/chain-DM3oH6Px.js +0 -8
  251. package/site/dist/assets/check-BPgcwEPP.js +0 -1
  252. package/site/dist/assets/chevron-down-BSsbXkK-.js +0 -1
  253. package/site/dist/assets/circle-alert-DWaeLZNj.js +0 -1
  254. package/site/dist/assets/circle-check-big-Chb1ivw9.js +0 -1
  255. package/site/dist/assets/circle-x-AV9Cet0R.js +0 -1
  256. package/site/dist/assets/client-CZG0ndth.js +0 -2
  257. package/site/dist/assets/client-Dsa7_3V3.js +0 -9
  258. package/site/dist/assets/coinbase-DCPC2L_R-D3aIgpyd.js +0 -3
  259. package/site/dist/assets/copy-Ckr0AkL6.js +0 -1
  260. package/site/dist/assets/core-snws5xwv.js +0 -12
  261. package/site/dist/assets/createLucideIcon-IPW50ckY.js +0 -1
  262. package/site/dist/assets/defineChain-CiuCXUZW.js +0 -1
  263. package/site/dist/assets/dijkstra-Drfl93V4.js +0 -1
  264. package/site/dist/assets/dist-Cm7pNAtL.js +0 -1
  265. package/site/dist/assets/dist-CoS6E-3x.js +0 -1
  266. package/site/dist/assets/dist-Dl07uGq1.js +0 -1
  267. package/site/dist/assets/dist-DnGANz51.js +0 -16
  268. package/site/dist/assets/embed.worker-ugGhEqCV.js +0 -1
  269. package/site/dist/assets/errors-BQ3N-RVt-BLqjTPNR.js +0 -1
  270. package/site/dist/assets/esm-B5cbIQaz.js +0 -37
  271. package/site/dist/assets/esm-ByALO6mA.js +0 -1
  272. package/site/dist/assets/esm-C3DslVJx.js +0 -1
  273. package/site/dist/assets/esm-Ci1ogKuK.js +0 -1
  274. package/site/dist/assets/esm-n2lWo7kz.js +0 -1
  275. package/site/dist/assets/ethers-zTjbodk5-wHhL_BQ1.js +0 -1
  276. package/site/dist/assets/eventemitter3-CL76jPsW.js +0 -1
  277. package/site/dist/assets/external-link-D-uehwM2.js +0 -1
  278. package/site/dist/assets/farcaster-DPlSjvF5-BqTvDXBR.js +0 -1
  279. package/site/dist/assets/features-DQ0UYy6K.js +0 -2
  280. package/site/dist/assets/fingerprint-pattern-D8ytA16Q.js +0 -1
  281. package/site/dist/assets/floating-ui.react-dom-DKWOL9UG.js +0 -1
  282. package/site/dist/assets/floating-ui.react-xRkl9O3u.js +0 -1
  283. package/site/dist/assets/formatErc20TokenAmount-BuPk9xcy-BVqOKQif.js +0 -1
  284. package/site/dist/assets/formatter-CVbp4SlJ.js +0 -1
  285. package/site/dist/assets/get-entropy-details-for-user-dHRH5Gpi-Dukf7_kJ.js +0 -1
  286. package/site/dist/assets/getChainName-DjpPdUSc-DUyKDrnp.js +0 -1
  287. package/site/dist/assets/getErc20Balance-jXNpFf0N-Bb9WM31M.js +0 -1
  288. package/site/dist/assets/getErc20TokenInfo-BNTrzLMP-KPrdn04K.js +0 -1
  289. package/site/dist/assets/getFormattedUsdFromLamports-De3U9GlO-Gx4_wxON.js +0 -1
  290. package/site/dist/assets/getUsdcMintAddress-BVgm9GRo-r2UnDeEy.js +0 -1
  291. package/site/dist/assets/guards-BIW-GiMJ-CHfXo8XI.js +0 -1
  292. package/site/dist/assets/hooks.module-BYZGfwDl.js +0 -1
  293. package/site/dist/assets/hourglass-CPG1HZmf.js +0 -1
  294. package/site/dist/assets/id-UqEwL2BI-BkKm533U.js +0 -1
  295. package/site/dist/assets/index-1HYFYA-W.js +0 -8
  296. package/site/dist/assets/index-BdEw5wyl-ChSJvPZk.js +0 -1
  297. package/site/dist/assets/index-CWARkn2w-Cezl_uBr.js +0 -61
  298. package/site/dist/assets/index-DvMddJcd-BatvgC-S.js +0 -12
  299. package/site/dist/assets/index-YiCw0Y_x-CX5i3Z87.js +0 -437
  300. package/site/dist/assets/index.browser-B-i-Lj3J.js +0 -1
  301. package/site/dist/assets/index.browser-BMmUcykv.js +0 -1
  302. package/site/dist/assets/index.es-BmqyoBEM.js +0 -2
  303. package/site/dist/assets/index.es-C1v1or--.js +0 -3
  304. package/site/dist/assets/isPaymentRequestAvailable-Bq1cemEn-By6r0dVB.js +0 -1
  305. package/site/dist/assets/jsx-runtime-BbDfbRii.js +0 -1
  306. package/site/dist/assets/landmark-CH6LV6F0.js +0 -1
  307. package/site/dist/assets/lib-BsS8z8Lj.js +0 -1
  308. package/site/dist/assets/localBatchGatewayRequest-3kLrX_c7.js +0 -32
  309. package/site/dist/assets/lock-DcskI4Bb.js +0 -1
  310. package/site/dist/assets/logger-CsXm3oIv-B0UYpNFI.js +0 -1
  311. package/site/dist/assets/mail-Bs0r5yOi.js +0 -1
  312. package/site/dist/assets/modal-context-CwmSeFcv-DruX9J1E.js +0 -1
  313. package/site/dist/assets/native-BKj27wfc.js +0 -1
  314. package/site/dist/assets/ofetch.CWycOUEr-CHp-crOQ.js +0 -1
  315. package/site/dist/assets/parseAbi-Csg-8t59.js +0 -2
  316. package/site/dist/assets/parseAbiParameters-BWbIMYe4.js +0 -1
  317. package/site/dist/assets/parseEther-VjhVo3dd.js +0 -1
  318. package/site/dist/assets/pkce-DXF0yKjP-ChQhE0st.js +0 -1
  319. package/site/dist/assets/preload-helper-uBIymjUX.js +0 -1
  320. package/site/dist/assets/privy-context-81KmsBw2-CjWGzyg7.js +0 -2
  321. package/site/dist/assets/property-DuZ9DLP4.js +0 -3
  322. package/site/dist/assets/react-DCXkXSrv.js +0 -1
  323. package/site/dist/assets/react-DQnMl2Xv.js +0 -1
  324. package/site/dist/assets/react-DrJ0MmMO.js +0 -1
  325. package/site/dist/assets/react-auth-sHnipzbS.js +0 -1
  326. package/site/dist/assets/react-dom-5dW7aKoP.js +0 -1
  327. package/site/dist/assets/reservoir-x-nGuZkT-BMX127i6.js +0 -1
  328. package/site/dist/assets/roles-32M1l9jI.js +0 -1
  329. package/site/dist/assets/rolldown-runtime-BcKkbAw3.js +0 -1
  330. package/site/dist/assets/safe-url-D7SRPu33-CkMedGro.js +0 -1
  331. package/site/dist/assets/secp256k1-C4nzcPo-.js +0 -1
  332. package/site/dist/assets/secp256k1-D92aGNnU.js +0 -1
  333. package/site/dist/assets/secp256k1-DmbwyFau.js +0 -1
  334. package/site/dist/assets/secp256k1-_MleGpqL.js +0 -1
  335. package/site/dist/assets/secp256k1-xl2og-wO.js +0 -1
  336. package/site/dist/assets/serializeAuthorizationList-CX8hGL56.js +0 -1
  337. package/site/dist/assets/serializeSignature-DfxkPnu-.js +0 -2
  338. package/site/dist/assets/sha2-BU_QyRTx.js +0 -1
  339. package/site/dist/assets/shared-DRZUmnL5-xqAUARWo.js +0 -193
  340. package/site/dist/assets/shared-FM0rljBt--Avg6q2R.js +0 -21
  341. package/site/dist/assets/shouldProceedtoEmbeddedWalletCreationFlow-B3vyavrh-BK_FBkaE.js +0 -1
  342. package/site/dist/assets/simulateTransaction-CfuOrae4-64lWYJAL.js +0 -1
  343. package/site/dist/assets/smartphone-CcEZ-XAy.js +0 -1
  344. package/site/dist/assets/src-BBjkfi-Q.js +0 -1
  345. package/site/dist/assets/storage-Ufl800Ne-D0bZBw-H.js +0 -9
  346. package/site/dist/assets/stringify-BlDm7tuc.js +0 -21
  347. package/site/dist/assets/stripe.esm-CZRDpc-N.js +0 -2
  348. package/site/dist/assets/styles-BsotlekN-CXEB4Ud9.js +0 -9
  349. package/site/dist/assets/styles-Cm9H63WO-Br4QtzUL.js +0 -92
  350. package/site/dist/assets/styles-DVyDvTdj-BTABn2K8.js +0 -74
  351. package/site/dist/assets/styles-hslXy6xG-D-49b1rk.js +0 -164
  352. package/site/dist/assets/telegram-B-JqnkqZ-RBoY3PLx.js +0 -1
  353. package/site/dist/assets/to-ui-error-DWe-qwS3-Cx2bk1kZ.js +0 -4
  354. package/site/dist/assets/toViemAccount-B5iDGq1b-Ba8L7zed.js +0 -2
  355. package/site/dist/assets/transaction-BNTP-bFm-M5tJx8vY.js +0 -1
  356. package/site/dist/assets/transfer-6YztDh-t-BVQkTvG-.js +0 -1
  357. package/site/dist/assets/transformers.web-CVP3pJOS.js +0 -40
  358. package/site/dist/assets/transformers.web-D8W1u_I2.js +0 -49
  359. package/site/dist/assets/triangle-alert-CWjYH75H.js +0 -1
  360. package/site/dist/assets/twitch-5IOe4sIQ-lDtgk0os.js +0 -1
  361. package/site/dist/assets/types-4CuOD23i.js +0 -1
  362. package/site/dist/assets/use-headless-crypto-deposit-Yn-QRLB3-LUIVFBSd.js +0 -1
  363. package/site/dist/assets/use-unlink-wallet-BKK4_5Ur-CC2pq27z.js +0 -1
  364. package/site/dist/assets/useActiveWallet-B-yZYGc6-D7Ik-LxJ.js +0 -123
  365. package/site/dist/assets/useGetSolPrice-x7gfUIHJ-D1hmQfpA.js +0 -1
  366. package/site/dist/assets/useGetTokenPrice-CaoUkb79-ZnbuJ-vP.js +0 -1
  367. package/site/dist/assets/useI18n-C-5n_noF-BcqC76yj.js +0 -1
  368. package/site/dist/assets/useWalletBalance-CZnP5f_f-Dw2GIJ4g.js +0 -1
  369. package/site/dist/assets/useWallets-X1ndvJ_U-DRjz7uYD.js +0 -1
  370. package/site/dist/assets/utils-BkR7EQjg.js +0 -1
  371. package/site/dist/assets/utils-DJhBNOZA.js +0 -1
  372. package/site/dist/assets/utils-DdckVfQr.js +0 -1
  373. package/site/dist/assets/w3m-modal-BeVrBOIf.js +0 -1174
  374. package/site/dist/assets/wallet-Bn85Sg2W.js +0 -1
  375. package/site/dist/assets/wallet-Bnzeiqed.js +0 -2
  376. package/site/dist/assets/wallet-connect-BvJtUsDt-Y03hhqFN.js +0 -1
  377. package/site/dist/assets/walletconnect-registry-xxG8XLCb-D1SSFvMp.js +0 -1
  378. package/site/dist/assets/walletconnect-v2-3frQFwbg-CrtLXofr.js +0 -12
  379. package/site/dist/assets/wui-ux-by-reown-D34ILNLS.js +0 -1147
  380. package/site/dist/assets/x-rlSp9U7W.js +0 -1
  381. package/src/discover/explore.js +0 -271
  382. package/src/market/settle.js +0 -165
  383. package/src/market/terms.js +0 -200
  384. package/src/publish/cli.js +0 -336
  385. package/src/publish/envelope.js +0 -185
  386. package/src/publish/manifest.js +0 -142
@@ -1,336 +0,0 @@
1
- #!/usr/bin/env node
2
- // The publisher's CLI. Everything an app's publish UI does, with a local key.
3
- //
4
- // WHY THIS IS NOT IN AN APP
5
- // -------------------------
6
- // A publisher should not have to clone the app they are publishing to. The
7
- // browser flow costs one file-picker click and one wallet confirmation PER FILE,
8
- // which makes seeding a library impossible and publishing from a chat message
9
- // impossible for the same reason — but the deeper problem is that it made the
10
- // app's repo a dependency of publishing to it.
11
- //
12
- // So this is a CLIENT of the relay's HTTP surface and of nothing else. The only
13
- // app-specific thing it needs is `ns` — the namespace folded into every
14
- // resourceId — and the relay declares that itself, so a publisher needs a URL, a
15
- // key, and a price.
16
- //
17
- // PUBLISHER_PRIVATE_KEY=0x… westmarch-publish <relay> <path…> [flags]
18
- //
19
- // --ns=name app namespace. Defaults to what the relay declares.
20
- // NEVER guessed: it is folded into every resourceId, so a
21
- // wrong one mints ids nobody can buy from and orphans a
22
- // library that already exists.
23
- // --dir=sub/folder stage under this folder instead of the library root
24
- // --price=1000 USDC base units per file (default: the relay's)
25
- // --desc="…" description for every file in this run
26
- // --catalog-only publish as FREE catalog entries: committed to the graph,
27
- // searchable, but never encrypted, uploaded or minted as a
28
- // resource. One commitStateRoot covers a graph of any size,
29
- // so this is what makes bulk ingest cost one transaction
30
- // instead of one per file.
31
- // --register send the one-time, fee-paying register() tx if needed
32
- // --setup onboard only — terms and registration. Publishes nothing,
33
- // and without --register sends nothing at all: it reports
34
- // what is missing and what it would cost. Ends with one
35
- // `SETUP <json>` line for a caller to parse.
36
- // --selfcheck run the offline self-check and exit
37
- //
38
- // A description is the highest-signal thing a file carries — for an image it is
39
- // the ONLY thing, since nothing in a JPEG's bytes says what it is. So per-file
40
- // descriptions come from a `<file>.txt` sidecar when one exists (--desc is the
41
- // fallback for the rest).
42
- //
43
- // The key comes from the environment and never from argv — argv is visible in
44
- // `ps` to every process on the box.
45
-
46
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
47
- import { basename, join, relative, resolve, sep } from "node:path";
48
- import { createPublicClient, createWalletClient, defineChain, http } from "viem";
49
- import { privateKeyToAccount } from "viem/accounts";
50
- import { encryptAndUpload } from "../market/settle.js";
51
- import { resourceIdFor } from "./envelope.js";
52
- import { newUid, readManifest, writeManifest } from "./manifest.js";
53
-
54
- // ── argv ──────────────────────────────────────────────────────────────────────
55
-
56
- const flags = {};
57
- const args = [];
58
- for (const a of process.argv.slice(2)) {
59
- const m = /^--([^=]+)(?:=([\s\S]*))?$/.exec(a);
60
- if (m) flags[m[1]] = m[2] ?? true;
61
- else args.push(a);
62
- }
63
-
64
- // ── file collection ───────────────────────────────────────────────────────────
65
-
66
- /** Sidecars and dotfiles are not products. A `.txt` NEXT TO a file it describes
67
- * is a caption; an orphan `.txt` is a document someone may want to sell. */
68
- export const skip = (name, isSidecar) =>
69
- name.startsWith(".") || name.endsWith(".vtt") || isSidecar;
70
-
71
- function collect(abs, root = abs) {
72
- const st = statSync(abs);
73
- if (st.isDirectory()) {
74
- return readdirSync(abs).sort().flatMap((n) => collect(join(abs, n), root));
75
- }
76
- const name = basename(abs);
77
- const described = name.endsWith(".txt") && existsSync(abs.slice(0, -4));
78
- if (skip(name, described)) return [];
79
- const rel = (root === abs ? name : relative(root, abs)).split(sep).join("/");
80
- return [{ abs, rel }];
81
- }
82
-
83
- /** `<file>.txt` beside the file, else --desc, else nothing. */
84
- const descFor = (abs) => {
85
- const side = `${abs}.txt`;
86
- if (existsSync(side)) return readFileSync(side, "utf8").trim().slice(0, 2000);
87
- return typeof flags.desc === "string" ? flags.desc : null;
88
- };
89
-
90
- // ── relay client ──────────────────────────────────────────────────────────────
91
-
92
- function client(relay) {
93
- let token = null;
94
- // Origin is sent explicitly and identically on every request: the relay
95
- // derives both the SIWE message's domain and the terms URL from it, so a
96
- // nonce issued under one origin and a terms acceptance under another would
97
- // sign two different documents.
98
- const headers = (extra = {}) => ({ Origin: relay, ...(token ? { Authorization: `Bearer ${token}` } : {}), ...extra });
99
- const j = async (res) => {
100
- const body = await res.json().catch(() => ({}));
101
- if (!res.ok) throw new Error(body.error ?? `${res.status} ${res.statusText}`);
102
- return body;
103
- };
104
- return {
105
- setToken: (t) => { token = t; },
106
- get: (path) => fetch(`${relay}${path}`, { headers: headers() }).then(j),
107
- post: (path, body) => fetch(`${relay}${path}`, {
108
- method: "POST", headers: headers({ "Content-Type": "application/json" }), body: JSON.stringify(body),
109
- }).then(j),
110
- prepare: (library) => fetch(`${relay}/api/publish/prepare`, {
111
- method: "POST", headers: headers({ "Content-Type": "application/json" }), body: JSON.stringify({ library }),
112
- }).then(j),
113
- };
114
- }
115
-
116
- // ── main ──────────────────────────────────────────────────────────────────────
117
-
118
- async function main() {
119
- const [relayArg, ...paths] = args;
120
- if (!relayArg) {
121
- console.error("usage: PUBLISHER_PRIVATE_KEY=0x… westmarch-publish <relay> <path…> [--ns=] [--dir=] [--price=] [--desc=] [--register] [--setup]");
122
- process.exit(2);
123
- }
124
- const relay = relayArg.replace(/\/$/, "");
125
-
126
- const key = process.env.PUBLISHER_PRIVATE_KEY;
127
- if (!/^0x[0-9a-fA-F]{64}$/.test(key ?? "")) throw new Error("PUBLISHER_PRIVATE_KEY must be a 0x-prefixed 32-byte hex key (env only — never argv)");
128
- const account = privateKeyToAccount(key);
129
- const api = client(relay);
130
-
131
- const cfg = await api.get("/api/config");
132
- if (cfg.readOnly) throw new Error(`${relay} is read-only — it serves a published catalog but accepts no publishes. Point at the relay for the app you are publishing to.`);
133
-
134
- // Never defaulted, never guessed. `ns` is folded into every resourceId this
135
- // wallet mints, so the wrong one produces ids that address nobody's bytes and
136
- // a manifest key that reads back as an empty library.
137
- const ns = typeof flags.ns === "string" ? flags.ns : cfg.ns;
138
- if (!ns) throw new Error(`${relay} does not declare an app namespace (config.ns) — pass --ns=<name> explicitly, and be sure it is the one this app already publishes under`);
139
-
140
- const chain = defineChain({
141
- id: cfg.chainId,
142
- name: `chain-${cfg.chainId}`,
143
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
144
- rpcUrls: { default: { http: [cfg.rpc] } },
145
- });
146
- const wallet = createWalletClient({ account, chain, transport: http(cfg.rpc) });
147
- const node = createPublicClient({ chain, transport: http(cfg.rpc) });
148
- const send = async (tx) => {
149
- const hash = await wallet.sendTransaction({ to: tx.to, data: tx.data, ...(tx.value ? { value: BigInt(tx.value) } : {}) });
150
- const rc = await node.waitForTransactionReceipt({ hash });
151
- if (rc.status !== "success") throw new Error(`tx reverted: ${hash}`);
152
- return hash;
153
- };
154
-
155
- // ── sign in ───────────────────────────────────────────────────────────────
156
- // This signature is also the rights attestation the relay appends to its log
157
- // before minting a session. Automating it does not make it mean less: the
158
- // key holder is asserting they are licensed to distribute what follows.
159
- const { nonce, message } = await api.post("/api/session/nonce", { address: account.address });
160
- const { token } = await api.post("/api/session", { nonce, signature: await account.signMessage({ message }) });
161
- api.setToken(token);
162
- console.log(`signed in as ${account.address} — publishing to ${relay} as "${ns}"`);
163
-
164
- // ── terms + registration ──────────────────────────────────────────────────
165
- let reg = await api.get("/api/registration");
166
- if (!reg.acceptedTerms) {
167
- const t = await api.get("/api/terms");
168
- await api.post("/api/terms/accept", { signature: await account.signMessage({ message: t.message }) });
169
- console.log(`accepted publisher terms (${t.hash.slice(0, 12)}…) — ${t.url}`);
170
- reg = await api.get("/api/registration");
171
- }
172
- if (!reg.registered) {
173
- // Registration costs a fee in ETH and is irreversible, so it is never
174
- // sent implicitly. Two txs, in the order the relay gave: global standing
175
- // in the data registry, then membership of this app. Both are needed —
176
- // commitStateRoot cross-calls the app registry, so stopping after the
177
- // first buys a revert at the end of the publish instead of an error here.
178
- const total = BigInt(reg.fee ?? 0) + BigInt(reg.appFee ?? 0);
179
- if (!flags.register) {
180
- if (!flags.setup) throw new Error(`${account.address} is not a registered publisher. Re-run with --register to send ${reg.txs.length} registration tx(s) (fee: ${total} wei), or register at https://fangorn.network`);
181
- } else {
182
- console.log(`registering as a publisher (${reg.txs.length} tx, fee ${total} wei)…`);
183
- for (const tx of reg.txs) await send(tx);
184
- reg = await api.get("/api/registration");
185
- }
186
- }
187
-
188
- // ── setup mode ────────────────────────────────────────────────────────────
189
- // Onboarding a publisher — terms, registration txs, and a bucket to put bytes
190
- // in — used to be reachable only by attempting a publish and reading the
191
- // error. A caller that wants to ASK "am I set up?" had nothing to call.
192
- //
193
- // Without --register it is a dry run: it says what is missing and what the
194
- // fees would be, and sends nothing. The last line is `SETUP <json>` so a
195
- // caller parses one line instead of scraping prose.
196
- if (flags.setup) {
197
- const state = { address: account.address, ns, registered: !!reg.registered, acceptedTerms: true };
198
- const w = await api.get("/api/worker").catch(() => null);
199
- // Null until this wallet is a registered publisher — the relay withholds
200
- // both, which is the only way storage can be "not ready" now.
201
- state.workerUrl = w?.workerUrl ?? null;
202
- state.storage = !!w?.workerUrl;
203
- if (!reg.registered) {
204
- state.fee = (BigInt(reg.fee ?? 0) + BigInt(reg.appFee ?? 0)).toString();
205
- state.txs = reg.txs?.length ?? 0;
206
- }
207
- console.log(`SETUP ${JSON.stringify(state)}`);
208
- return;
209
- }
210
-
211
- // ── stage ─────────────────────────────────────────────────────────────────
212
- const under = typeof flags.dir === "string" ? flags.dir.replace(/^\/+|\/+$/g, "") : "";
213
- const files = paths.flatMap((p) => collect(resolve(p)));
214
- if (paths.length && !files.length) throw new Error("nothing to upload — every path was a dotfile, a .vtt or a description sidecar");
215
-
216
- // The manifest lives in the bucket, not on the relay, so this client reads
217
- // it, edits it and writes it back. Both the worker and this wallet's upload
218
- // token come from the relay, which stores neither: the URL is its own config
219
- // and the token is derived from the address.
220
- const { workerUrl, uploadToken } = await api.get("/api/worker");
221
- if (!workerUrl) throw new Error(`${account.address} has no storage — it is not a registered Fangorn publisher yet (run with --setup --register, or sign up at https://fangorn.network)`);
222
- const store = { ns, workerUrl, uploadToken, owner: account.address };
223
- const manifest = await readManifest(store);
224
-
225
- for (const f of files) {
226
- const rel = under ? `${under}/${f.rel}` : f.rel;
227
- const entry = (manifest.files[rel] ??= { uid: newUid() });
228
- if (flags["catalog-only"]) entry.forSale = false;
229
- else if (flags.price) entry.price = String(flags.price);
230
- const desc = descFor(f.abs);
231
- if (desc) entry.desc = desc;
232
-
233
- // A free catalog entry is committed to the graph but never encrypted,
234
- // uploaded or minted — which is what makes bulk ingest cost one commit
235
- // instead of one createResource per file.
236
- if (entry.forSale === false) {
237
- console.log(`↑ ${rel} … ok (free entry)${desc ? " (described)" : ""}`);
238
- continue;
239
- }
240
- process.stdout.write(`↑ ${rel} … `);
241
- const out = await encryptAndUpload({
242
- file: f.abs, workerUrl, uploadToken,
243
- resourceId: entry.published?.resourceId ?? resourceIdFor(ns, account.address, entry.uid),
244
- });
245
- entry.published = { ...out, resourceId: entry.published?.resourceId, workerUrl, mime: entry.mime };
246
- // Written per file: a run that dies partway must not lose the pointers
247
- // already paid for in bandwidth.
248
- await writeManifest({ ...store, manifest });
249
- console.log(`ok${desc ? " (described)" : ""}`);
250
- }
251
-
252
- // ── publish ───────────────────────────────────────────────────────────────
253
- const library = Object.entries(manifest.files).map(([path, f]) => ({
254
- path, type: "video", uid: f.uid, price: f.price, mime: f.mime,
255
- desc: f.desc, forSale: f.forSale, cues: f.cues, published: f.published,
256
- }));
257
- const prep = await api.prepare(library);
258
-
259
- for (const [i, c] of prep.creates.entries()) {
260
- console.log(`createResource ${i + 1}/${prep.creates.length} — ${c.path}`);
261
- await send(c);
262
- }
263
-
264
- // No vectors. A publisher builds and publishes the graph; embedding is not
265
- // its job and never was. The commit route still takes a `vectors` map because
266
- // an app's own browser flow may fill it, and an empty one is the ordinary
267
- // case, not a degraded one.
268
- // The pin is authorized by the publisher's own signature against their own
269
- // storage quota. The challenge carries an Issued-At the gate only honours for
270
- // a few minutes, so it is fetched here and not at the start of the run.
271
- //
272
- // `storageGate: null` is not a broken relay — it is one pinning directly on
273
- // its own account, which ignores storageAuth entirely. Asking a gate that
274
- // isn't there for a challenge failed a publish the relay would have accepted,
275
- // and blamed a service that was never in the path.
276
- let storageAuth;
277
- if (cfg.storageGate) {
278
- const gate = await fetch(cfg.storageGate, {
279
- method: "POST", headers: { "content-type": "application/json" },
280
- body: JSON.stringify({ address: account.address, size: 1 }),
281
- }).then((r) => r.json()).catch(() => ({}));
282
- if (!gate.challenge) throw new Error(`storage gate issued no challenge — ${cfg.storageGate} may be down`);
283
- storageAuth = { message: gate.challenge, signature: await account.signMessage({ message: gate.challenge }) };
284
- } else {
285
- console.log("relay pins on its own account — no storage gate to authorize against");
286
- }
287
-
288
- const sealed = await api.post("/api/publish/commit", { vectors: {}, storageAuth });
289
- console.log("commitStateRoot…");
290
- await send(sealed.commitTx);
291
- for (const [path, ptr] of Object.entries(sealed.published)) {
292
- manifest.files[path].published = { ...(manifest.files[path].published ?? {}), ...ptr };
293
- }
294
- await writeManifest({ ...store, manifest });
295
- await api.post("/api/settle", {});
296
-
297
- const { vertices, edges } = sealed.staged;
298
- console.log(`published — ${vertices} vertices, ${edges} edges`);
299
- for (const [path, ptr] of Object.entries(sealed.published)) console.log(` ${path} ${ptr.resourceId}`);
300
- }
301
-
302
- // ── self-check: `node src/publish/cli.js --selfcheck` (offline) ──────────────────
303
-
304
- if (flags.selfcheck) {
305
- const assert = (cond, msg) => { if (!cond) throw new Error(msg); };
306
-
307
- // Collection: sidecars are descriptions, not products; a .txt with no file
308
- // beside it still sells; nested paths keep their shape.
309
- const here = new Set(["/lib/a.jpg", "/lib/notes.txt", "/lib/sub/b.mp3"]);
310
- const survives = (name, abs) => !skip(name, name.endsWith(".txt") && here.has(abs.slice(0, -4)));
311
- assert(survives("a.jpg", "/lib/a.jpg"), "a real file was skipped");
312
- assert(!survives("a.jpg.txt", "/lib/a.jpg.txt"), "a description sidecar was published as a file");
313
- assert(survives("notes.txt", "/lib/notes.txt"), "an orphan .txt should publish as a text file");
314
- assert(!survives(".hidden", "/lib/.hidden"), "a dotfile was published");
315
- assert(!survives("c.mp4.vtt", "/lib/c.mp4.vtt"), "a .vtt was published — /api/upload rejects these");
316
-
317
- // A read-only relay and a relay that declares no namespace must both fail
318
- // BEFORE anything is signed, uploaded or sent — and say which it was.
319
- const real = globalThis.fetch;
320
- const runAgainst = async (config) => {
321
- globalThis.fetch = async (url) => String(url).endsWith("/api/config")
322
- ? { ok: true, json: async () => config }
323
- : { ok: false, json: async () => ({ error: "should never be reached" }) };
324
- process.env.PUBLISHER_PRIVATE_KEY = `0x${"11".repeat(32)}`;
325
- args.length = 0; args.push("http://relay", "/tmp");
326
- return main().then(() => "no error", (e) => e.message);
327
- };
328
- try {
329
- assert(/read-only/.test(await runAgainst({ readOnly: true })), "a read-only relay must be refused by name");
330
- assert(/namespace/.test(await runAgainst({ chainId: 1, rpc: "http://r" })), "a relay declaring no ns must be refused, not guessed at");
331
- } finally { globalThis.fetch = real; delete process.env.PUBLISHER_PRIVATE_KEY; }
332
-
333
- console.log("cli.js self-check ok — sidecars, read-only relay refused, missing ns refused");
334
- } else {
335
- await main().catch((e) => { console.error(`✗ ${e.message}`); process.exit(1); });
336
- }
@@ -1,185 +0,0 @@
1
- // The on-the-wire envelope format, shared by everything that writes it.
2
- //
3
- // `ns` — the app's namespace string — is the first argument of the two id
4
- // derivations and it is NOT defaulted. It is folded into every resourceId ever
5
- // minted, so it is permanent: changing an app's ns orphans everything that app
6
- // already published, and two apps that accidentally share one mint into each
7
- // other's id space. An app pins it once (sond3r: src/pay/envelope.js) and never
8
- // again. A default here would be a way to pick it by accident.
9
- //
10
- // Three implementations used to need these constants to agree — server/settle.js
11
- // (relay-side publish), src/pay/encrypt.js (browser-side publish) and src/pay/stream.js /
12
- // src/pay/buy.js (the buyer, unpacking). They agree because they import from here.
13
- // A mismatch is not a crash: chunk `i` decrypts to garbage, or a key lands where
14
- // the worker's /access gate isn't looking, and the file goes dark for everyone
15
- // who paid for it.
16
- //
17
- // Browser-safe on purpose: viem only, no node builtins.
18
- import { concat, encodePacked, hexToBytes, keccak256, numberToHex, stringToBytes } from "viem";
19
-
20
- /** The bytes32 the publisher hands createResource. Stable per manifest uid. */
21
- export const uidHashFor = (ns, uid) => keccak256(stringToBytes(`${ns}:${uid}`));
22
-
23
- /** What that uid becomes on-chain: keccak(publisher ++ uid), derived by the
24
- * registry (`resource_id_of`) and reproduced here. Two publishers with the same
25
- * uid get two different resources, which is what makes an id impossible to
26
- * squat — and it means this derivation must match the contract exactly, or a
27
- * publish uploads ciphertext to a key no on-chain resource points at.
28
- *
29
- * It lives here rather than in server/index.js because the BROWSER derives it
30
- * too now (it names the R2 key it uploads ciphertext to) and the server derives
31
- * it when minting the resource. Two copies that disagreed would put the bytes
32
- * and the resource at different ids, and the file would be unbuyable. Pinned
33
- * against the registry's own view in sond3r's server/index.js --selfcheck. */
34
- export const resourceIdFor = (ns, owner, uid) => keccak256(concat([owner, uidHashFor(ns, uid)]));
35
-
36
- export const NONCE_LEN = 12;
37
-
38
- /** 64MiB — under Cloudflare's 100MB body cap with room for nonce/tag/headers. */
39
- export const CHUNK_SIZE = 64 * 1024 * 1024;
40
-
41
- /** R2 key for chunk `i`. Chunk 0 is the resourceId itself, so the sealed DEK
42
- * lands exactly where the worker's /access gate looks for it and a small file
43
- * is still a single object. Every buyer-side unpacker must match this. */
44
- export const chunkKey = (resourceId, i) =>
45
- i === 0 ? resourceId : keccak256(encodePacked(["bytes32", "uint32"], [resourceId, i]));
46
-
47
- /** AES-GCM additional data: the chunk index, big-endian. MUST match src/pay/buy.js. */
48
- export const aadFor = (i) => hexToBytes(numberToHex(i, { size: 4 }));
49
-
50
- /**
51
- * Compress-then-encrypt: what gets sealed is `flag(1) || bytes`, flag 1 = gzip.
52
- * ponytail: gzip only when it actually shrinks. mp4/webm/jpeg are already
53
- * compressed and gzip *grows* them, so the flag makes the store-raw case the
54
- * fallback instead of a 0.03% tax. MUST match unpack() in src/pay/stream.js.
55
- */
56
- export async function pack(plain) {
57
- const gz = new Uint8Array(await new Response(
58
- new Response(plain).body.pipeThrough(new CompressionStream("gzip")),
59
- ).arrayBuffer());
60
- const win = gz.length < plain.length;
61
- const out = new Uint8Array((win ? gz.length : plain.length) + 1);
62
- out[0] = win ? 1 : 0;
63
- out.set(win ? gz : plain, 1);
64
- return out;
65
- }
66
-
67
- /**
68
- * The inverse of pack(). It lives here, beside its inverse, because a packer and
69
- * an unpacker in two repos is a format with two definitions — and the failure is
70
- * not a crash but a file that uploads fine and is undecryptable forever.
71
- */
72
- export async function unpack(bytes) {
73
- if (bytes[0] === 0) return bytes.subarray(1);
74
- // Not 0/1 means these bytes aren't packed at all — almost always ciphertext
75
- // uploaded before the compression format landed. The publisher re-publishes;
76
- // there is nothing the buyer can do.
77
- if (bytes[0] !== 1) throw new Error(`unknown chunk encoding ${bytes[0]} — this file needs re-publishing`);
78
- return new Uint8Array(await new Response(
79
- new Response(bytes.subarray(1)).body.pipeThrough(new DecompressionStream("gzip")),
80
- ).arrayBuffer());
81
- }
82
-
83
- /** GET the worker's static X25519 pubkey (what DEKs are sealed to). */
84
- export async function getWorkerPubkey(workerUrl) {
85
- const res = await fetch(`${workerUrl}/pubkey`);
86
- if (!res.ok) throw new Error(`/pubkey failed: ${res.status}`);
87
- const { pubkey } = await res.json();
88
- return hexToBytes(pubkey);
89
- }
90
-
91
- /**
92
- * POST one already-encrypted chunk to the access worker's R2.
93
- *
94
- * `sealedDek` is omitted for objects that are the publisher's own state rather
95
- * than something a buyer will pay for — the worker then writes no DEK object, so
96
- * /access has nothing to release. See handleUpload in the worker.
97
- */
98
- export async function putChunk({ workerUrl, uploadToken, key, body, sealedDek, immutable = false }) {
99
- const res = await fetch(`${workerUrl}/upload/${key}`, {
100
- method: "POST",
101
- headers: {
102
- "Content-Type": "application/octet-stream",
103
- ...(sealedDek ? { "X-Sealed-Dek": sealedDek } : {}),
104
- // Only for a key that IS its content's digest. A resourceId is derived
105
- // from (publisher, uid), so a re-publish rewrites it with new bytes
106
- // under a new DEK, and an edge still serving the old ones hands every
107
- // buyer ciphertext that decrypts to garbage. A shard named by its own
108
- // sha256 cannot have that problem, and is the reason this flag exists.
109
- ...(immutable ? { "X-Immutable": "1" } : {}),
110
- // The worker's upload gate. On a fresh publisher-owned worker the
111
- // first request carrying this claims the bucket to it.
112
- ...(uploadToken ? { Authorization: `Bearer ${uploadToken}` } : {}),
113
- },
114
- body,
115
- });
116
- if (!res.ok) {
117
- const text = await res.text();
118
- // A quota refusal is the one upload failure a publisher can act on, so its
119
- // message is passed through instead of buried in a URL and a status code.
120
- const reason = (() => { try { return JSON.parse(text).error; } catch { return null; } })();
121
- throw new Error(res.status === 413 && reason ? reason : `${workerUrl}/upload/${key}: ${res.status} ${text}`);
122
- }
123
- return res;
124
- }
125
-
126
- /**
127
- * Delete every chunk of a published resource, and its sealed DEK, from the
128
- * worker. Returns the number of chunks it asked the worker to drop.
129
- *
130
- * The chunk count comes from the caller's manifest rather than from the worker,
131
- * because the worker deliberately won't synthesize keys (see handleDelete in
132
- * webworker/fangorn-access-worker) — the key derivation lives here, next to the
133
- * upload that used it.
134
- *
135
- * Failures are RAISED, not swallowed. A delete that half-worked must not be
136
- * reported as freed space: the operator is still paying for whatever is left,
137
- * and silently crediting the publisher for it is how the storage ledger drifts
138
- * away from the R2 bill.
139
- */
140
- export async function deleteResource({ workerUrl, uploadToken, resourceId, chunks = 1 }) {
141
- for (let i = 0; i < Math.max(1, chunks); i++) {
142
- const res = await fetch(`${workerUrl}/upload/${chunkKey(resourceId, i)}`, {
143
- method: "DELETE",
144
- headers: uploadToken ? { Authorization: `Bearer ${uploadToken}` } : {},
145
- });
146
- // 404 is a worker too old to have the route; that's a real failure, since
147
- // nothing was deleted and the caller is about to free the allowance.
148
- if (!res.ok) throw new Error(`${workerUrl} refused to delete chunk ${i} of ${resourceId}: ${res.status} ${res.statusText}`);
149
- }
150
- return Math.max(1, chunks);
151
- }
152
-
153
- // ── self-check: `node src/publish/envelope.js` ───────────────────────────────────
154
- //
155
- // These vectors are PINNED to the values sond3r's own envelope.js produced before
156
- // this file was extracted from it, with ns = "sond3r". They are not a style test:
157
- // every resourceId ever minted by fangorn.tv is derived by this function, and a
158
- // change here that shifts one byte makes ciphertext already in R2 unreachable from
159
- // the resource anyone paid for. If this fails, the extraction broke money.
160
- if (typeof process !== "undefined" && import.meta.url === `file://${process.argv[1]}` && process.argv[1].endsWith("/envelope.js")) {
161
- const owner = `0x${"11".repeat(20)}`;
162
- const uid = "Show/S1/a.mp4";
163
-
164
- const h = uidHashFor("sond3r", uid);
165
- if (h !== "0x855b20df257479ad7d5d5be0bcddc8c8554bff19edb33adb818872dd3d9e0d04") {
166
- throw new Error(`uidHashFor drifted: ${h}`);
167
- }
168
- const r = resourceIdFor("sond3r", owner, uid);
169
- if (r !== "0x0f919abdf691fae94b36395e67f7ed3301f0faea6027ffe609612656556a28a5") {
170
- throw new Error(`resourceIdFor drifted: ${r}`);
171
- }
172
-
173
- // The namespace is what separates two apps' id spaces. Same owner, same uid,
174
- // different app → different resource, or one app could mint over another's.
175
- if (resourceIdFor("wiki", owner, uid) === r) throw new Error("ns does not reach the resourceId");
176
- if (uidHashFor("wiki", uid) === h) throw new Error("ns does not reach the uid hash");
177
-
178
- // Chunk 0 IS the resourceId — the worker's /access gate looks for the sealed
179
- // DEK there and nowhere else.
180
- if (chunkKey(r, 0) !== r) throw new Error("chunk 0 must be the resourceId itself");
181
- if (chunkKey(r, 1) === r) throw new Error("chunk 1 collided with chunk 0");
182
- if (aadFor(1).length !== 4) throw new Error("aad must be 4 bytes, big-endian");
183
-
184
- console.log("envelope.js self-check ok — pinned sond3r ids, ns separation, chunk keys");
185
- }