@needmoretruth/nmts-cli 0.17.2 → 0.34.2

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 (419) hide show
  1. package/AGENTS.md +125 -517
  2. package/CHANGELOG.md +104 -0
  3. package/README.ko.md +377 -469
  4. package/README.md +394 -474
  5. package/dist/account-proof.d.ts +29 -0
  6. package/dist/account-proof.js +6 -6
  7. package/dist/account.d.ts +18 -0
  8. package/dist/account.js +5 -5
  9. package/dist/agent-host.d.ts +71 -0
  10. package/dist/agent-host.js +169 -0
  11. package/dist/api-advice.d.ts +2 -0
  12. package/dist/api-advice.js +210 -0
  13. package/dist/api-key.d.ts +79 -0
  14. package/dist/api-key.js +7 -7
  15. package/dist/api-refusal.d.ts +14 -0
  16. package/dist/api-refusal.js +8 -0
  17. package/dist/api.d.ts +114 -0
  18. package/dist/api.js +59 -162
  19. package/dist/args.d.ts +160 -0
  20. package/dist/args.js +118 -30
  21. package/dist/artifact-about.d.ts +49 -0
  22. package/dist/autonomy.d.ts +39 -0
  23. package/dist/autonomy.js +110 -32
  24. package/dist/code-access.d.ts +29 -0
  25. package/dist/code-access.js +3 -3
  26. package/dist/code-vault.d.ts +38 -0
  27. package/dist/code-vault.js +8 -8
  28. package/dist/collision.d.ts +67 -0
  29. package/dist/collision.js +1 -1
  30. package/dist/commands/accept-terms.d.ts +11 -0
  31. package/dist/commands/accept-terms.js +106 -0
  32. package/dist/commands/balance.d.ts +7 -0
  33. package/dist/commands/balance.js +54 -1
  34. package/dist/commands/create-code-file.d.ts +24 -0
  35. package/dist/commands/create-code-file.js +93 -0
  36. package/dist/commands/create-link.d.ts +10 -0
  37. package/dist/commands/create-link.js +232 -0
  38. package/dist/commands/create.d.ts +20 -0
  39. package/dist/commands/create.js +43 -102
  40. package/dist/commands/credits.d.ts +9 -0
  41. package/dist/commands/credits.js +110 -0
  42. package/dist/commands/delete-account.d.ts +11 -0
  43. package/dist/commands/delete-account.js +63 -0
  44. package/dist/commands/deposit.d.ts +7 -0
  45. package/dist/commands/deposit.js +85 -0
  46. package/dist/commands/devices.d.ts +12 -0
  47. package/dist/commands/devices.js +144 -0
  48. package/dist/commands/documents.d.ts +35 -0
  49. package/dist/commands/documents.js +212 -0
  50. package/dist/commands/env.d.ts +5 -0
  51. package/dist/commands/env.js +10 -2
  52. package/dist/commands/erase.d.ts +13 -0
  53. package/dist/commands/erase.js +193 -0
  54. package/dist/commands/expiring.d.ts +19 -0
  55. package/dist/commands/extend.d.ts +33 -0
  56. package/dist/commands/extend.js +54 -15
  57. package/dist/commands/get.d.ts +18 -0
  58. package/dist/commands/key-manage.d.ts +11 -0
  59. package/dist/commands/key-manage.js +162 -0
  60. package/dist/commands/key.d.ts +28 -0
  61. package/dist/commands/key.js +249 -0
  62. package/dist/commands/kit.d.ts +11 -0
  63. package/dist/commands/kit.js +7 -7
  64. package/dist/commands/listfile.d.ts +13 -0
  65. package/dist/commands/listfile.js +12 -5
  66. package/dist/commands/login.d.ts +18 -0
  67. package/dist/commands/login.js +19 -19
  68. package/dist/commands/logout.d.ts +1 -0
  69. package/dist/commands/logout.js +1 -1
  70. package/dist/commands/losses.d.ts +12 -0
  71. package/dist/commands/losses.js +180 -0
  72. package/dist/commands/ls.d.ts +23 -0
  73. package/dist/commands/marks-dispatch.d.ts +2 -0
  74. package/dist/commands/marks-dispatch.js +51 -0
  75. package/dist/commands/marks.d.ts +46 -0
  76. package/dist/commands/marks.js +80 -0
  77. package/dist/commands/mcp.d.ts +24 -0
  78. package/dist/commands/mcp.js +70 -5
  79. package/dist/commands/mode.d.ts +8 -0
  80. package/dist/commands/mode.js +60 -24
  81. package/dist/commands/on-collision.d.ts +6 -0
  82. package/dist/commands/organise.d.ts +46 -0
  83. package/dist/commands/padding.d.ts +7 -0
  84. package/dist/commands/padding.js +114 -0
  85. package/dist/commands/public-code.d.ts +9 -0
  86. package/dist/commands/public-code.js +8 -8
  87. package/dist/commands/pull.d.ts +27 -0
  88. package/dist/commands/push-wallet.d.ts +19 -0
  89. package/dist/commands/push-wallet.js +69 -0
  90. package/dist/commands/push.d.ts +47 -0
  91. package/dist/commands/push.js +37 -7
  92. package/dist/commands/put-wallet.d.ts +77 -0
  93. package/dist/commands/put-wallet.js +316 -0
  94. package/dist/commands/put.d.ts +56 -0
  95. package/dist/commands/put.js +53 -6
  96. package/dist/commands/rebuild.d.ts +18 -0
  97. package/dist/commands/rebuild.js +108 -10
  98. package/dist/commands/receive.d.ts +24 -0
  99. package/dist/commands/recovery-dispatch.d.ts +2 -0
  100. package/dist/commands/recovery-dispatch.js +34 -0
  101. package/dist/commands/recovery-list.d.ts +11 -0
  102. package/dist/commands/recovery-list.js +6 -6
  103. package/dist/commands/recovery.d.ts +25 -0
  104. package/dist/commands/recovery.js +2 -2
  105. package/dist/commands/rollback.d.ts +9 -0
  106. package/dist/commands/rollback.js +118 -0
  107. package/dist/commands/s3.d.ts +23 -0
  108. package/dist/commands/s3.js +6 -6
  109. package/dist/commands/settings.d.ts +5 -0
  110. package/dist/commands/settings.js +21 -6
  111. package/dist/commands/share-dispatch.d.ts +2 -0
  112. package/dist/commands/share-dispatch.js +46 -0
  113. package/dist/commands/share.d.ts +14 -0
  114. package/dist/commands/share.js +13 -3
  115. package/dist/commands/shares-sent.d.ts +18 -0
  116. package/dist/commands/shares-sent.js +95 -0
  117. package/dist/commands/support.d.ts +24 -0
  118. package/dist/commands/support.js +321 -0
  119. package/dist/commands/sweep.d.ts +11 -0
  120. package/dist/commands/tip.d.ts +11 -0
  121. package/dist/commands/tip.js +86 -0
  122. package/dist/commands/trash.d.ts +17 -0
  123. package/dist/commands/trash.js +1 -1
  124. package/dist/commands/trial.d.ts +7 -0
  125. package/dist/commands/trial.js +16 -18
  126. package/dist/commands/unlock.d.ts +16 -0
  127. package/dist/commands/unlock.js +159 -0
  128. package/dist/commands/update.d.ts +42 -0
  129. package/dist/commands/update.js +1 -1
  130. package/dist/commands/usage.d.ts +8 -0
  131. package/dist/commands/verify.d.ts +18 -0
  132. package/dist/commands/verify.js +10 -6
  133. package/dist/commands/wallet-activity.d.ts +26 -0
  134. package/dist/commands/wallet-activity.js +117 -0
  135. package/dist/commands/wallet-donate.d.ts +26 -0
  136. package/dist/commands/wallet-donate.js +163 -0
  137. package/dist/commands/wallet-hall.d.ts +35 -0
  138. package/dist/commands/wallet-hall.js +214 -0
  139. package/dist/commands/wallet-send.d.ts +22 -0
  140. package/dist/commands/wallet-send.js +153 -0
  141. package/dist/commands/wallet-storage-ops.d.ts +24 -0
  142. package/dist/commands/wallet-storage-ops.js +215 -0
  143. package/dist/commands/wallet-storage.d.ts +18 -0
  144. package/dist/commands/wallet-storage.js +95 -0
  145. package/dist/commands/wallet-swap.d.ts +32 -0
  146. package/dist/commands/wallet-swap.js +287 -0
  147. package/dist/commands/wallet.d.ts +36 -0
  148. package/dist/commands/wallet.js +57 -15
  149. package/dist/commands/whoami.d.ts +10 -0
  150. package/dist/commands/whoami.js +24 -6
  151. package/dist/consent.d.ts +140 -0
  152. package/dist/consent.js +89 -30
  153. package/dist/credentials.d.ts +146 -0
  154. package/dist/credentials.js +10 -10
  155. package/dist/crypto-surface.d.ts +197 -0
  156. package/dist/crypto.d.ts +81 -0
  157. package/dist/crypto.js +13 -0
  158. package/dist/deposit.d.ts +25 -0
  159. package/dist/deposit.js +78 -0
  160. package/dist/download-part.d.ts +40 -0
  161. package/dist/download-sink.d.ts +76 -0
  162. package/dist/download.d.ts +79 -0
  163. package/dist/download.js +3 -3
  164. package/dist/drive-paths.d.ts +73 -0
  165. package/dist/environment.d.ts +62 -0
  166. package/dist/environment.js +29 -9
  167. package/dist/errors.d.ts +22 -0
  168. package/dist/errors.js +3 -3
  169. package/dist/exit.d.ts +44 -0
  170. package/dist/expiry.d.ts +58 -0
  171. package/dist/extend-budget.d.ts +35 -0
  172. package/dist/extend-budget.js +81 -0
  173. package/dist/extend-chain.d.ts +43 -0
  174. package/dist/extend-chain.js +40 -3
  175. package/dist/extend-plan.d.ts +148 -0
  176. package/dist/extend-plan.js +1 -1
  177. package/dist/gate.d.ts +18 -0
  178. package/dist/gate.js +100 -0
  179. package/dist/guards.d.ts +2 -0
  180. package/dist/help.d.ts +20 -0
  181. package/dist/help.js +199 -33
  182. package/dist/human-check.d.ts +23 -0
  183. package/dist/index.d.ts +42 -0
  184. package/dist/index.js +49 -0
  185. package/dist/item-trash.d.ts +1 -0
  186. package/dist/kit-file.d.ts +56 -0
  187. package/dist/kit-file.js +10 -10
  188. package/dist/list-file.d.ts +88 -0
  189. package/dist/list-file.js +90 -7
  190. package/dist/list-view-find.d.ts +15 -0
  191. package/dist/list-view-order.d.ts +32 -0
  192. package/dist/main.d.ts +12 -0
  193. package/dist/main.js +112 -113
  194. package/dist/manifest-chunk-cache.d.ts +14 -0
  195. package/dist/manifest-chunk-cache.js +103 -0
  196. package/dist/manifest-chunk-flow.d.ts +81 -0
  197. package/dist/manifest-chunk-flow.js +307 -0
  198. package/dist/manifest-create.d.ts +23 -0
  199. package/dist/manifest-create.js +26 -30
  200. package/dist/manifest-write.d.ts +161 -0
  201. package/dist/manifest-write.js +42 -33
  202. package/dist/manifest.d.ts +82 -0
  203. package/dist/manifest.js +69 -21
  204. package/dist/mark-render.d.ts +26 -0
  205. package/dist/mcp-args.d.ts +17 -0
  206. package/dist/mcp-ask.d.ts +43 -0
  207. package/dist/mcp-ask.js +100 -0
  208. package/dist/mcp-tools/context.d.ts +46 -0
  209. package/dist/mcp-tools/credits.d.ts +3 -0
  210. package/dist/mcp-tools/credits.js +61 -0
  211. package/dist/mcp-tools/files.d.ts +3 -0
  212. package/dist/mcp-tools/files.js +34 -0
  213. package/dist/mcp-tools/organise.d.ts +3 -0
  214. package/dist/mcp-tools/organise.js +82 -2
  215. package/dist/mcp-tools/reads.d.ts +3 -0
  216. package/dist/mcp-tools/reads.js +132 -4
  217. package/dist/mcp-tools/share.d.ts +3 -0
  218. package/dist/mcp-tools/share.js +26 -7
  219. package/dist/mcp-tools/support.d.ts +3 -0
  220. package/dist/mcp-tools/support.js +140 -0
  221. package/dist/mcp-tools/tiers.d.ts +31 -0
  222. package/dist/mcp-tools/tiers.js +147 -0
  223. package/dist/mcp.d.ts +80 -0
  224. package/dist/mcp.js +110 -9
  225. package/dist/net-retry.d.ts +38 -0
  226. package/dist/network.d.ts +10 -0
  227. package/dist/notice.d.ts +7 -0
  228. package/dist/notice.js +7 -7
  229. package/dist/product.d.ts +29 -0
  230. package/dist/product.js +1 -1
  231. package/dist/progress.d.ts +37 -0
  232. package/dist/prompt.d.ts +31 -0
  233. package/dist/prompt.js +1 -1
  234. package/dist/rebuild-key-check.d.ts +26 -0
  235. package/dist/rebuild-key-check.js +90 -0
  236. package/dist/rebuild.d.ts +87 -0
  237. package/dist/rebuild.js +26 -4
  238. package/dist/recovery-assemble.d.ts +40 -0
  239. package/dist/recovery-assemble.js +1 -1
  240. package/dist/recovery-build.d.ts +40 -0
  241. package/dist/recovery-map-file.d.ts +53 -0
  242. package/dist/recovery-map-file.js +4 -4
  243. package/dist/recovery-map.d.ts +197 -0
  244. package/dist/recovery-release.d.ts +104 -0
  245. package/dist/recovery-seq.d.ts +10 -0
  246. package/dist/recovery-source.d.ts +50 -0
  247. package/dist/redact.d.ts +70 -0
  248. package/dist/redact.js +212 -0
  249. package/dist/registration.d.ts +30 -0
  250. package/dist/registration.js +5 -5
  251. package/dist/risk.d.ts +380 -0
  252. package/dist/risk.js +206 -0
  253. package/dist/run-log.d.ts +78 -0
  254. package/dist/run-log.js +301 -0
  255. package/dist/s3/listing.d.ts +47 -0
  256. package/dist/s3/multipart.d.ts +21 -0
  257. package/dist/s3/response-sink.d.ts +8 -0
  258. package/dist/s3/same-file.d.ts +54 -0
  259. package/dist/s3/server.d.ts +58 -0
  260. package/dist/s3/server.js +2 -2
  261. package/dist/s3/sigv4.d.ts +46 -0
  262. package/dist/s3/sigv4.js +1 -1
  263. package/dist/s3/staging.d.ts +10 -0
  264. package/dist/s3/xml.d.ts +28 -0
  265. package/dist/safe-path.d.ts +41 -0
  266. package/dist/seal.d.ts +120 -0
  267. package/dist/secret-reader.d.ts +28 -0
  268. package/dist/server.d.ts +9 -0
  269. package/dist/session.d.ts +32 -0
  270. package/dist/session.js +1 -1
  271. package/dist/setup-questions.d.ts +7 -0
  272. package/dist/share.d.ts +108 -0
  273. package/dist/share.js +1 -1
  274. package/dist/shared/lib/crypto/size-padding.d.ts +55 -0
  275. package/dist/shared/lib/crypto/size-padding.js +5 -0
  276. package/dist/shared/lib/drive/manifest-chunks.d.ts +95 -0
  277. package/dist/shared/lib/drive/manifest-chunks.js +252 -0
  278. package/dist/shared/lib/drive/manifest-codec.d.ts +214 -0
  279. package/dist/shared/lib/drive/manifest-codec.js +7 -7
  280. package/dist/shared/lib/drive/manifest-index.d.ts +98 -0
  281. package/dist/shared/lib/drive/manifest-ops.d.ts +167 -0
  282. package/dist/shared/lib/drive/manifest-ops.js +9 -4
  283. package/dist/shared/lib/drive/manifest-pack.d.ts +65 -0
  284. package/dist/shared/lib/drive/manifest-pack.js +287 -0
  285. package/dist/shared/lib/drive/manifest-settings.d.ts +113 -0
  286. package/dist/shared/lib/drive/manifest-settings.js +87 -6
  287. package/dist/shared/lib/drive/name-conflict.d.ts +76 -0
  288. package/dist/shared/lib/drive/rebuild-verify.d.ts +73 -0
  289. package/dist/shared/lib/drive/rebuild-verify.js +112 -0
  290. package/dist/shared/lib/drive/unique-name.d.ts +9 -0
  291. package/dist/shared/lib/drive/zstd.d.ts +27 -0
  292. package/dist/shared/lib/drive/zstd.js +74 -0
  293. package/dist/shared/lib/extend/epochs.d.ts +205 -0
  294. package/dist/shared/lib/net/retry-budget.d.ts +79 -0
  295. package/dist/shared/lib/share/shared-file-info.d.ts +21 -0
  296. package/dist/shared/lib/storage-control/chain.d.ts +57 -0
  297. package/dist/shared/lib/storage-control/chain.js +82 -0
  298. package/dist/shared/lib/storage-control/plan.d.ts +40 -0
  299. package/dist/shared/lib/storage-control/plan.js +72 -0
  300. package/dist/shared/lib/storage-control/reuse.d.ts +72 -0
  301. package/dist/shared/lib/storage-control/reuse.js +95 -0
  302. package/dist/shared/lib/storage-network.d.ts +25 -0
  303. package/dist/shared/lib/upload/part-plan.d.ts +24 -0
  304. package/dist/shared/lib/wallet/activity.d.ts +138 -0
  305. package/dist/shared/lib/wallet/activity.js +208 -0
  306. package/dist/shared/lib/wallet/send-rules.d.ts +68 -0
  307. package/dist/shared/lib/wallet/send-rules.js +116 -0
  308. package/dist/shared/lib/wallet/swap-rules.d.ts +115 -0
  309. package/dist/shared/lib/wallet/swap-rules.js +199 -0
  310. package/dist/shared/lib/wallet/tip.d.ts +14 -0
  311. package/dist/shared/lib/wallet/tip.js +34 -0
  312. package/dist/shared/lib/wallet/venue-ids.d.ts +23 -0
  313. package/dist/shared/lib/wallet/venue-ids.js +53 -0
  314. package/dist/standing-tip.d.ts +24 -0
  315. package/dist/standing-tip.js +52 -0
  316. package/dist/stdout.d.ts +38 -0
  317. package/dist/storage-control-chain.d.ts +38 -0
  318. package/dist/storage-control-chain.js +78 -0
  319. package/dist/sui-rpc.d.ts +15 -0
  320. package/dist/sui-rpc.js +59 -0
  321. package/dist/support-attach.d.ts +24 -0
  322. package/dist/support-attach.js +72 -0
  323. package/dist/support-copy.d.ts +42 -0
  324. package/dist/support-copy.js +109 -0
  325. package/dist/support-wire.d.ts +49 -0
  326. package/dist/support-wire.js +194 -0
  327. package/dist/trash-sweep.d.ts +55 -0
  328. package/dist/units.d.ts +2 -0
  329. package/dist/update-check.d.ts +50 -0
  330. package/dist/update-source.d.ts +63 -0
  331. package/dist/upload-api.d.ts +3 -0
  332. package/dist/upload-file.d.ts +97 -0
  333. package/dist/upload-file.js +3 -2
  334. package/dist/upload-price.d.ts +50 -0
  335. package/dist/upload-steps.d.ts +26 -0
  336. package/dist/upload-steps.js +29 -9
  337. package/dist/upload-store.d.ts +182 -0
  338. package/dist/upload-wallet-chain.d.ts +54 -0
  339. package/dist/upload-wallet-chain.js +203 -0
  340. package/dist/upload-wallet-plan.d.ts +128 -0
  341. package/dist/upload-wallet-plan.js +184 -0
  342. package/dist/upload-wallet.d.ts +24 -0
  343. package/dist/upload-wallet.js +208 -0
  344. package/dist/upload-wire.d.ts +244 -0
  345. package/dist/upload.d.ts +18 -0
  346. package/dist/upload.js +10 -0
  347. package/dist/usage-report.d.ts +35 -0
  348. package/dist/wallet-activity-chain.d.ts +5 -0
  349. package/dist/wallet-activity-chain.js +31 -0
  350. package/dist/wallet-chain.d.ts +19 -0
  351. package/dist/wallet-chain.js +2 -2
  352. package/dist/wallet-grant.d.ts +50 -0
  353. package/dist/wallet-grant.js +218 -0
  354. package/dist/wallet-send-chain.d.ts +24 -0
  355. package/dist/wallet-send-chain.js +49 -0
  356. package/dist/wallet-sign.d.ts +133 -0
  357. package/dist/wallet-sign.js +317 -0
  358. package/dist/wallet-storage-chain.d.ts +3 -0
  359. package/dist/wallet-storage-chain.js +20 -0
  360. package/dist/wallet-swap-chain.d.ts +52 -0
  361. package/dist/wallet-swap-chain.js +186 -0
  362. package/dist/wallet-swap-quote.d.ts +22 -0
  363. package/dist/wallet-swap-quote.js +126 -0
  364. package/dist/wallet.d.ts +135 -0
  365. package/dist/wallet.js +5 -5
  366. package/dist/walrus-write.d.ts +56 -0
  367. package/dist/walrus-write.js +5 -4
  368. package/dist/walrus.d.ts +86 -0
  369. package/dist/walrus.js +48 -10
  370. package/dist/zstd-node.d.ts +11 -0
  371. package/dist/zstd-node.js +71 -0
  372. package/docs/commands/accept-terms.md +15 -0
  373. package/docs/commands/create.md +17 -0
  374. package/docs/commands/credits.md +34 -0
  375. package/docs/commands/delete-account.md +14 -0
  376. package/docs/commands/deposit.md +27 -0
  377. package/docs/commands/devices.md +9 -0
  378. package/docs/commands/documents.md +10 -0
  379. package/docs/commands/env.md +21 -0
  380. package/docs/commands/extend.md +27 -0
  381. package/docs/commands/folders.md +10 -0
  382. package/docs/commands/get.md +14 -0
  383. package/docs/commands/key.md +14 -0
  384. package/docs/commands/login.md +23 -0
  385. package/docs/commands/logout.md +11 -0
  386. package/docs/commands/losses.md +13 -0
  387. package/docs/commands/ls.md +16 -0
  388. package/docs/commands/marks.md +9 -0
  389. package/docs/commands/mcp.md +33 -0
  390. package/docs/commands/mode.md +29 -0
  391. package/docs/commands/on-collision.md +13 -0
  392. package/docs/commands/padding.md +13 -0
  393. package/docs/commands/public-code.md +9 -0
  394. package/docs/commands/pull.md +9 -0
  395. package/docs/commands/push.md +12 -0
  396. package/docs/commands/put.md +36 -0
  397. package/docs/commands/rebuild.md +13 -0
  398. package/docs/commands/recovery.md +18 -0
  399. package/docs/commands/s3.md +17 -0
  400. package/docs/commands/share.md +15 -0
  401. package/docs/commands/support.md +16 -0
  402. package/docs/commands/tip.md +21 -0
  403. package/docs/commands/trash.md +25 -0
  404. package/docs/commands/trial.md +12 -0
  405. package/docs/commands/unlock.md +25 -0
  406. package/docs/commands/update.md +14 -0
  407. package/docs/commands/usage.md +14 -0
  408. package/docs/commands/verify.md +18 -0
  409. package/docs/commands/wallet.md +67 -0
  410. package/docs/commands/whoami.md +12 -0
  411. package/package.json +67 -8
  412. package/server.json +28 -0
  413. package/vendor/nmts-crypto/nmts_crypto_wasm.d.ts +23 -2
  414. package/vendor/nmts-crypto/nmts_crypto_wasm.js +44 -2
  415. package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm +0 -0
  416. package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm.d.ts +1 -0
  417. package/vendor/nmts-crypto/package.json +1 -1
  418. package/dist/commands/consent.js +0 -77
  419. package/dist/extend-sign.js +0 -130
@@ -0,0 +1,126 @@
1
+ // A venue's answer, read from the chain: how much comes out now, what stays unused, and the fee the
2
+ // quote's own numbers say the venue charges. ⛔ EVERYTHING HERE IS A READ — `devInspect`, no gas,
3
+ // no key, no signature, and a zero address as the nominal sender so a quote needs no funded wallet.
4
+ //
5
+ // ⛔ THE ARITHMETIC IS THE BROWSER'S, copied byte-for-byte (`shared/lib/wallet/swap-rules.ts`); this
6
+ // file only asks the chain and hands the answers to it. DeepBook's fee has no constant: it is
7
+ // MEASURED by quoting the same amount with the fee taken from the input coin and again with the
8
+ // fee paid in DEEP, then comparing output per unit actually spent. Bluefin's quote reports its
9
+ // fee outright (`fee_amount` + `protocol_fee`; both parts count).
10
+ //
11
+ // ⛔ A FEE RATE THAT CANNOT BE MEASURED IS NULL, NEVER 0. A quote that cannot be read THROWS — the
12
+ // command says "this venue did not answer", never a number in its place. An output of 0 is an
13
+ // answer, not a failure: the amount is below one lot, or the book is empty.
14
+ import { bcs } from "@mysten/sui/bcs";
15
+ import { Transaction } from "@mysten/sui/transactions";
16
+ import { deepbookRowFrom, feeRateBpsOf, measureDeepbookFeeBps, } from "./shared/lib/wallet/swap-rules.js";
17
+ import { BLUEFIN_MAX_SQRT_PRICE, BLUEFIN_MIN_SQRT_PRICE, DEEPBOOK_PACKAGE_IDS, DEEPBOOK_WAL_SUI_POOLS, } from "./shared/lib/wallet/venue-ids.js";
18
+ import { SUI_COIN_TYPE, walCoinType } from "./wallet.js";
19
+ /** The zero address: a quote is a read, and a read needs no wallet. */
20
+ export const QUOTE_SENDER = `0x${"0".repeat(64)}`;
21
+ function readU64(value) {
22
+ return BigInt(bcs.U64.parse(Uint8Array.from(value[0])));
23
+ }
24
+ /**
25
+ * The FRONT of Bluefin's `SwapResult`, in the order its `pool.move` declares them. BCS reads from
26
+ * the front, so the fields behind these need not be known. The first three are checked against the
27
+ * question asked; if they do not echo it, the rest cannot be trusted either.
28
+ */
29
+ const BluefinSwapResultPrefix = bcs.struct("BluefinSwapResultPrefix", {
30
+ a2b: bcs.bool(),
31
+ by_amount_in: bcs.bool(),
32
+ amount_specified: bcs.u64(),
33
+ amount_specified_remaining: bcs.u64(),
34
+ amount_calculated: bcs.u64(),
35
+ fee_growth_global: bcs.u128(),
36
+ fee_amount: bcs.u64(),
37
+ protocol_fee: bcs.u64(),
38
+ });
39
+ async function quoteBluefin(client, network, direction, amountInUnits, binding) {
40
+ const a2b = direction === "WAL_TO_SUI";
41
+ const tx = new Transaction();
42
+ tx.moveCall({
43
+ target: `${binding.packageId}::pool::calculate_swap_results`,
44
+ typeArguments: [walCoinType(network), SUI_COIN_TYPE],
45
+ arguments: [
46
+ tx.object(binding.poolId),
47
+ tx.pure.bool(a2b),
48
+ tx.pure.bool(true),
49
+ tx.pure.u64(amountInUnits),
50
+ tx.pure.u128(a2b ? BLUEFIN_MIN_SQRT_PRICE : BLUEFIN_MAX_SQRT_PRICE),
51
+ ],
52
+ });
53
+ const res = await client.devInspectTransactionBlock({ sender: QUOTE_SENDER, transactionBlock: tx });
54
+ if (res.error)
55
+ throw new Error(`the Bluefin quote was refused on chain: ${res.error}`);
56
+ const returned = res.results?.[0]?.returnValues;
57
+ const first = returned?.[0];
58
+ if (first === undefined)
59
+ throw new Error("the Bluefin quote came back with no value");
60
+ const result = BluefinSwapResultPrefix.parse(Uint8Array.from(first[0]));
61
+ if (result.a2b !== a2b || result.by_amount_in !== true || BigInt(result.amount_specified) !== amountInUnits) {
62
+ throw new Error("the Bluefin quote did not echo the question it was asked");
63
+ }
64
+ return {
65
+ venue: "bluefin",
66
+ outUnits: BigInt(result.amount_calculated),
67
+ feeRateBps: feeRateBpsOf(BigInt(result.fee_amount) + BigInt(result.protocol_fee), amountInUnits),
68
+ leftoverInUnits: BigInt(result.amount_specified_remaining),
69
+ };
70
+ }
71
+ async function quoteDeepbook(client, network, direction, amountInUnits) {
72
+ const pkg = DEEPBOOK_PACKAGE_IDS[network];
73
+ const pool = DEEPBOOK_WAL_SUI_POOLS[network];
74
+ const types = [walCoinType(network), SUI_COIN_TYPE];
75
+ const inputFeeFn = direction === "SUI_TO_WAL" ? "get_base_quantity_out_input_fee" : "get_quote_quantity_out_input_fee";
76
+ const deepModeArgs = direction === "SUI_TO_WAL" ? [0n, amountInUnits] : [amountInUnits, 0n];
77
+ const build = (withDeepMode) => {
78
+ const tx = new Transaction();
79
+ tx.moveCall({
80
+ target: `${pkg}::pool::${inputFeeFn}`,
81
+ typeArguments: types,
82
+ arguments: [tx.object(pool), tx.pure.u64(amountInUnits), tx.object.clock()],
83
+ });
84
+ if (withDeepMode) {
85
+ tx.moveCall({
86
+ target: `${pkg}::pool::get_quantity_out`,
87
+ typeArguments: types,
88
+ arguments: [tx.object(pool), tx.pure.u64(deepModeArgs[0]), tx.pure.u64(deepModeArgs[1]), tx.object.clock()],
89
+ });
90
+ }
91
+ return tx;
92
+ };
93
+ // Both modes in ONE inspection so they see the same book. If the DEEP-mode call is refused, the
94
+ // quote is asked again alone: what comes out matters more than what the fee was.
95
+ let res = await client.devInspectTransactionBlock({ sender: QUOTE_SENDER, transactionBlock: build(true) });
96
+ let deepModeAnswered = true;
97
+ if (res.error) {
98
+ deepModeAnswered = false;
99
+ res = await client.devInspectTransactionBlock({ sender: QUOTE_SENDER, transactionBlock: build(false) });
100
+ }
101
+ if (res.error)
102
+ throw new Error(`the DeepBook quote was refused on chain: ${res.error}`);
103
+ const rowOf = (values) => values !== undefined && values.length >= 3 && values[0] !== undefined && values[1] !== undefined
104
+ ? deepbookRowFrom(direction, readU64(values[0]), readU64(values[1]))
105
+ : null;
106
+ const inputMode = rowOf(res.results?.[0]?.returnValues);
107
+ if (inputMode === null)
108
+ throw new Error("the DeepBook quote came back in a shape this tool does not read");
109
+ const deepMode = deepModeAnswered ? rowOf(res.results?.[1]?.returnValues) : null;
110
+ return {
111
+ venue: "deepbook",
112
+ outUnits: inputMode.outUnits,
113
+ feeRateBps: measureDeepbookFeeBps(amountInUnits, inputMode, deepMode),
114
+ leftoverInUnits: inputMode.leftoverInUnits,
115
+ };
116
+ }
117
+ /** One venue's answer now. Bluefin needs its resolved binding; DeepBook needs nothing resolved. */
118
+ export async function quoteVenue(client, network, venue, direction, amountInUnits, bluefin) {
119
+ if (amountInUnits <= 0n)
120
+ throw new Error("a swap quote needs an amount above zero");
121
+ if (venue === "deepbook")
122
+ return quoteDeepbook(client, network, direction, amountInUnits);
123
+ if (bluefin === null)
124
+ throw new Error("Bluefin's package was not resolved");
125
+ return quoteBluefin(client, network, direction, amountInUnits, bluefin);
126
+ }
@@ -0,0 +1,135 @@
1
+ import { type CryptoGlue } from "./crypto.ts";
2
+ import type { Network } from "./network.ts";
3
+ /**
4
+ * The wallet this NMTS key opens by itself.
5
+ *
6
+ * ⛔ IT IS WALLET 0 BECAUSE THAT IS THE ONE THE BROWSER OPENS. Every wallet, including this one,
7
+ * comes out of `wallet_seed_for` — there is no special case for the first — so the index is the
8
+ * whole of the difference between "the account's wallet" and somebody else's.
9
+ *
10
+ * ⛔ EXPORTED SO THE SIGNER CANNOT PICK ITS OWN. `extend-sign.ts` derives a keypair from the same
11
+ * root and has to reach the SAME wallet as the address printed here; a second literal `0` over
12
+ * there would be a second answer to a question with one right one, and the failure is silent —
13
+ * a signature from an address nobody funded. `extend-sign.test.ts` compares the two.
14
+ */
15
+ export declare const BUILT_IN_WALLET_INDEX = 0;
16
+ /** The chain's own coin. Its type is fixed by the chain itself and takes no network. */
17
+ export declare const SUI_COIN_TYPE = "0x2::sui::SUI";
18
+ /**
19
+ * The storage network's coin, per chain.
20
+ *
21
+ * ⛔ A WRONG VALUE HERE READS AS AN EMPTY WALLET RATHER THAN AS A FAULT. Measured against the
22
+ * public mainnet node this tool reads from, 2026-08-24: asking for a coin type that does not
23
+ * exist answers `totalBalance: "0"` — the same answer a real empty balance gives. That
24
+ * measurement is why `readBalances` never reports a zero WAL balance without first asking the
25
+ * chain whether it has heard of the type at all; see `ChainReader.knowsCoinType`.
26
+ *
27
+ * ⚠ THIS IS A SECOND COPY. The browser app carries the same two strings for the same reason, and
28
+ * no machine holds the two level — this package deliberately imports nothing from that tree.
29
+ * What keeps them honest is the check above: a copy that went stale stops answering zero and
30
+ * starts saying it could not be read.
31
+ */
32
+ export declare const WAL_COIN_TYPES: Readonly<Record<Network, string>>;
33
+ /** The WAL coin type for one network. */
34
+ export declare function walCoinType(network: Network): string;
35
+ /**
36
+ * The Sui address of one 32-byte wallet seed.
37
+ *
38
+ * ⛔ IT IS THE SDK'S COMPUTATION, NOT ONE WRITTEN HERE. An address is BLAKE2b-256 over a scheme
39
+ * byte and the public key; hand-rolling that would put a second answer in the world for a
40
+ * question that must have exactly one, and the failure would be silent — a well-formed address
41
+ * nobody can spend from.
42
+ *
43
+ * ⚠ THE KEYPAIR HOLDS THE SECRET AND CANNOT BE WIPED. `@mysten/sui` keeps the key inside its own
44
+ * object, with no method that clears it; the same is true in the browser's worker. What is
45
+ * controlled here is lifetime: it is built, asked one question, and dropped, and the caller's
46
+ * copy of the seed is zeroed. Nothing retains it.
47
+ *
48
+ * Exported because the vectors that hold this to the other implementations of the same derivation
49
+ * are (seed, address) pairs, and a test cannot check them through the account-code entry point.
50
+ */
51
+ export declare function addressFromSeed(seed: Uint8Array): string;
52
+ /**
53
+ * The address of the wallet this NMTS key derives. Offline: nothing is asked of anybody.
54
+ *
55
+ * The same address on every network — an account has one wallet, and which chain it is looked up
56
+ * on is a separate question from what it is called.
57
+ */
58
+ export declare function walletAddress(code: string): Promise<string>;
59
+ /**
60
+ * The engine functions this module uses. Narrowed to the one it actually calls.
61
+ *
62
+ * ⛔ NOT A SEAM FOR REPLACING THE ENGINE. Every address this tool prints comes from the real
63
+ * WebAssembly, and a fake derivation would only prove this file agrees with a fake. The narrow
64
+ * type exists so a test can WRAP the real function and watch what was handed to it — which is
65
+ * the only way the wiping below can be checked at all, and the wiping is the part that matters.
66
+ */
67
+ export type WalletGlue = Pick<CryptoGlue, "wallet_seed_for">;
68
+ /**
69
+ * The NMTS key wallet's address, from a buffer the caller already holds.
70
+ *
71
+ * ⛔ IT TAKES KEY MATERIAL AND RETURNS A STRING. `derived` belongs to the caller and is left
72
+ * alone; the two secrets this function makes — the wallet root it slices out and the seed the
73
+ * engine expands from it — are wiped before it returns, on the failing path as well as the
74
+ * good one. Neither one leaves.
75
+ */
76
+ export declare function addressFromDerived(glue: WalletGlue, derived: Uint8Array): string;
77
+ /**
78
+ * How much of one coin an address holds, exactly as the chain reported it — or why nobody knows.
79
+ *
80
+ * ⛔ THE TWO CASES ARE DIFFERENT TYPES ON PURPOSE. A number with a "did it work" flag beside it is
81
+ * a number somebody will read without checking the flag. This shape makes the check unavoidable.
82
+ */
83
+ export type CoinBalance = {
84
+ readonly read: true;
85
+ readonly baseUnits: bigint;
86
+ } | {
87
+ readonly read: false;
88
+ readonly why: string;
89
+ };
90
+ /** Both balances of one wallet, each answering for itself. */
91
+ export interface WalletBalances {
92
+ readonly sui: CoinBalance;
93
+ readonly wal: CoinBalance;
94
+ }
95
+ /**
96
+ * The chain, as this tool needs it: two questions, no writing.
97
+ *
98
+ * ⛔ A SEAM, NOT AN OPTION. There is no flag that reaches it and no way to supply one from a
99
+ * command line. It exists so the arithmetic and every failure branch below can be driven by
100
+ * `node --test` — a test that needed a live chain could not run offline, would answer
101
+ * differently every day, and could not produce a refusal on demand at all.
102
+ */
103
+ export interface ChainReader {
104
+ /** Total held of one coin type, in base units. Throws, with the reason, when it cannot be read. */
105
+ totalOf(coinType: string): Promise<bigint>;
106
+ /**
107
+ * Has this chain heard of the coin type at all?
108
+ *
109
+ * ⛔ THE ONLY QUESTION THAT TELLS AN EMPTY WALLET FROM A COIN TYPE THIS BUILD HAS WRONG. Both
110
+ * answer "0" to the balance question — see the note on `WAL_COIN_TYPES`.
111
+ */
112
+ knowsCoinType(coinType: string): Promise<boolean>;
113
+ }
114
+ /**
115
+ * Read both balances, keeping "could not be read" apart from "empty" in every branch.
116
+ *
117
+ * ⚠ THE REASON A READ FAILED IS SAFE TO PRINT, and that is worth saying because in this tool it
118
+ * usually is not (`errors.ts`). Nothing secret is in one of these requests: what goes out is a
119
+ * public address and a coin type, so what comes back cannot quote a secret.
120
+ *
121
+ * ⛔ ONE COIN'S FAILURE DOES NOT TAKE THE OTHER DOWN. They are separate requests about separate
122
+ * coins, and an answer for one of them is worth more than a matching pair of silences.
123
+ */
124
+ export declare function readBalances(reader: ChainReader, walType: string): Promise<WalletBalances>;
125
+ /**
126
+ * Base units as a person reads them — the exact value, never rounded.
127
+ *
128
+ * ⛔ NOT `toFixed`, NOT A SHORTENED FORM. A wallet holding 0.000000004 SUI is not holding "0.00",
129
+ * and money that reads as zero when it is not is the one rounding error nobody forgives. The
130
+ * fractional part is trimmed of trailing zeros only, which removes nothing that was there.
131
+ *
132
+ * Callers hand it a total the chain reported; a negative one is refused where it is read, so this
133
+ * never sees one.
134
+ */
135
+ export declare function coinAmount(baseUnits: bigint): string;
package/dist/wallet.js CHANGED
@@ -1,4 +1,4 @@
1
- // The wallet an account code derives: its address, and what a chain says it holds.
1
+ // The wallet an NMTS key derives: its address, and what a chain says it holds.
2
2
  //
3
3
  // ⛔ NOTHING HERE SIGNS OR SPENDS, AND NOTHING HERE HANDS OUT A KEY. The only secret this module
4
4
  // touches is the 32-byte wallet seed; it lives for the length of one address computation and is
@@ -19,7 +19,7 @@ import { assertUsableCode } from "./account.js";
19
19
  import { DERIVED, loadCrypto } from "./crypto.js";
20
20
  import { NmtsError } from "./errors.js";
21
21
  /**
22
- * The wallet this account code opens by itself.
22
+ * The wallet this NMTS key opens by itself.
23
23
  *
24
24
  * ⛔ IT IS WALLET 0 BECAUSE THAT IS THE ONE THE BROWSER OPENS. Every wallet, including this one,
25
25
  * comes out of `wallet_seed_for` — there is no special case for the first — so the index is the
@@ -83,7 +83,7 @@ export function addressFromSeed(seed) {
83
83
  return Ed25519Keypair.fromSecretKey(seed).toSuiAddress();
84
84
  }
85
85
  /**
86
- * The address of the wallet this account code derives. Offline: nothing is asked of anybody.
86
+ * The address of the wallet this NMTS key derives. Offline: nothing is asked of anybody.
87
87
  *
88
88
  * The same address on every network — an account has one wallet, and which chain it is looked up
89
89
  * on is a separate question from what it is called.
@@ -101,7 +101,7 @@ export async function walletAddress(code) {
101
101
  // ⛔ Unreachable in practice — the same parser accepted this input a line ago. It is caught
102
102
  // anyway because the alternative is an engine message going out verbatim, and an engine
103
103
  // message about a code can carry the code (see `errors.ts`).
104
- throw new NmtsError("The account code could not be read on this machine.", { exitCode: 1 });
104
+ throw new NmtsError("The NMTS key could not be read on this machine.", { exitCode: 1 });
105
105
  }
106
106
  // ⛔ THIS BUFFER IS EVERY KEY IN THE ACCOUNT, not just the wallet root: the sign-in secret, the
107
107
  // key that opens the files, the key that opens the file list. It is wiped below along with
@@ -116,7 +116,7 @@ export async function walletAddress(code) {
116
116
  }
117
117
  }
118
118
  /**
119
- * The built-in wallet's address, from a buffer the caller already holds.
119
+ * The NMTS key wallet's address, from a buffer the caller already holds.
120
120
  *
121
121
  * ⛔ IT TAKES KEY MATERIAL AND RETURNS A STRING. `derived` belongs to the caller and is left
122
122
  * alone; the two secrets this function makes — the wallet root it slices out and the seed the
@@ -0,0 +1,56 @@
1
+ import { type EpochClock } from "./expiry.ts";
2
+ import type { BlobProtocol } from "./upload-wire.ts";
3
+ /**
4
+ * How long the relay gets for one blob PUT, sized to the body.
5
+ *
6
+ * The SDK's own default is 30 seconds, which a multi-megabyte body cannot finish on an ordinary
7
+ * connection — and a timeout here happens AFTER the storage is paid for.
8
+ */
9
+ export declare function relayTimeoutMs(bodyBytes: number): number;
10
+ /**
11
+ * Build a protocol client bound to ONE relay, for ONE upload of a known size.
12
+ *
13
+ * `onSent` is called as the request body leaves — that is the honest measure of an upload, and it
14
+ * is the only phase of one that anything can report on.
15
+ */
16
+ export declare function createBlobProtocol(network: string, bodyBytes: number, onSent?: (sent: number, total: number) => void): BlobProtocol & {
17
+ relayUrl: string;
18
+ };
19
+ /**
20
+ * The storage network's current epoch, or `null` when it could not be read.
21
+ *
22
+ * ⚠ ADVISORY ONLY. It becomes the `expiry_epoch` the server records beside the file, which the
23
+ * chain — not this number — is the authority on. `null` is written as 0, meaning "not recorded",
24
+ * which is honest; inventing a number would put a date in the drive that nothing stands behind.
25
+ */
26
+ export declare function readCurrentEpoch(network: string): Promise<number | null>;
27
+ /**
28
+ * The storage network's epoch clock: which epoch, how long one lasts, and when this one began.
29
+ *
30
+ * ⛔ THE EPOCH LENGTH IS READ, NEVER ASSUMED. It is one day on one network and fourteen on the
31
+ * other, so a constant borrowed from either would turn "fourteen days left" into "196 days
32
+ * left" on the wrong one — beside a sentence about a file being deleted.
33
+ *
34
+ * ⚠ `startedMs` is usually ABSENT and that is normal, not a failure: the network only carries the
35
+ * moment an epoch settled while it is not changing epochs. Everything downstream treats its
36
+ * absence as "the day count is a lower bound", which is the safe direction.
37
+ *
38
+ * `null` means the clock could not be read at all. ⛔ The caller must say so rather than draw a
39
+ * drive with nothing expiring — an unread clock and an account in no danger look identical from
40
+ * the outside and are the opposite of each other.
41
+ */
42
+ export declare function readEpochWindow(network: string): Promise<EpochClock | null>;
43
+ /**
44
+ * When the current epoch began, or null.
45
+ *
46
+ * ⛔ THE NARROWING ITSELF IS NOT WRITTEN HERE (2026-08-25). It used to be, and it accepted a single
47
+ * enum case while the network sits in a different one for nearly all of every epoch — so the
48
+ * anchor was thrown away almost always and this tool reported "N days or more" where it could
49
+ * have reported a date. The browser had the same bug in its own copy of the same judgement,
50
+ * which is the point: two narrowings are two answers. It now lives beside the arithmetic that
51
+ * depends on it, in the file both programs copy from.
52
+ *
53
+ * ⛔ STILL EXPORTED FROM HERE. `extend-chain.ts` reads the same state for a different reason and
54
+ * imports this name; re-exporting keeps one import path for callers in this package.
55
+ */
56
+ export { epochStartedMs } from "./shared/lib/extend/epochs.ts";
@@ -16,7 +16,8 @@ import { epochClock } from "./expiry.js";
16
16
  import { epochStartedMs as readEpochStart } from "./shared/lib/extend/epochs.js";
17
17
  import { NmtsError } from "./errors.js";
18
18
  import { countingFetch } from "./progress.js";
19
- import { relayHost, suiRpcHost } from "./walrus.js";
19
+ import { relayHost } from "./walrus.js";
20
+ import { suiRpcTransport } from "./sui-rpc.js";
20
21
  /**
21
22
  * How long the relay gets for one blob PUT, sized to the body.
22
23
  *
@@ -45,7 +46,7 @@ function extend(network, relayUrl, bodyBytes, onSent) {
45
46
  // ⛔ The network name reaches the SDK as well as the URL. A mirror pointed at the wrong chain
46
47
  // would otherwise be discovered as a blob the storage nodes refuse, after the money moved.
47
48
  network: network === "mainnet" ? "mainnet" : "testnet",
48
- url: suiRpcHost(network),
49
+ transport: suiRpcTransport(network),
49
50
  });
50
51
  return base.$extend(walrus({
51
52
  uploadRelay: {
@@ -128,7 +129,7 @@ export async function readCurrentEpoch(network) {
128
129
  try {
129
130
  const base = new SuiJsonRpcClient({
130
131
  network: network === "mainnet" ? "mainnet" : "testnet",
131
- url: suiRpcHost(network),
132
+ transport: suiRpcTransport(network),
132
133
  }).$extend(walrus({}));
133
134
  // The epoch lives on the COMMITTEE, not beside it: the system state describes capacity and
134
135
  // the deny lists as well, and only the committee is stamped with which epoch it serves.
@@ -159,7 +160,7 @@ export async function readEpochWindow(network) {
159
160
  try {
160
161
  const base = new SuiJsonRpcClient({
161
162
  network: network === "mainnet" ? "mainnet" : "testnet",
162
- url: suiRpcHost(network),
163
+ transport: suiRpcTransport(network),
163
164
  }).$extend(walrus({}));
164
165
  const [system, staking] = await Promise.all([base.walrus.systemState(), base.walrus.stakingState()]);
165
166
  // Both numbers can arrive as strings — Sui reports 64-bit values that way — so they are
@@ -0,0 +1,86 @@
1
+ /** Curated Walrus aggregator (read) endpoints per network, preference order. */
2
+ export declare const AGGREGATOR_HOSTS: Readonly<Record<string, readonly string[]>>;
3
+ /**
4
+ * Curated Walrus upload-relay (write) endpoints per network, preference order.
5
+ *
6
+ * ⛔ A RELAY IS NOT AN AGGREGATOR, and the difference is money. The relay is named inside the
7
+ * register transaction the server pays a tip in, so the bytes have to go to the SAME host the
8
+ * reservation was made for. That is why a write picks its host BEFORE the storage is bought and
9
+ * then never moves: failing over to a second relay would push bytes nobody paid that relay for.
10
+ */
11
+ export declare const RELAY_HOSTS: Readonly<Record<string, readonly string[]>>;
12
+ /**
13
+ * Sui JSON-RPC endpoints per network — public mirrors, because the official full nodes retired
14
+ * JSON-RPC on both networks (browser measurements 2026-07-29 testnet, 2026-08-03 mainnet).
15
+ *
16
+ * ⛔ READ-ONLY, AND NOT TRUSTED WITH ANYTHING. This tool asks one question here: how many shards
17
+ * the storage network currently has, which the erasure coding needs. A wrong answer produces a
18
+ * blob id the storage network refuses, so it fails loudly at the relay rather than quietly
19
+ * storing something unreadable. No key is ever sent to one of these, and nothing is signed.
20
+ *
21
+ * ⭐ 2026-09-01 — a LIST per network, two operators, first-that-answers. It used to be one host,
22
+ * and the testnet one was measured dead that morning: `rpc-testnet.suiscan.xyz` completes the
23
+ * TCP handshake in 31 ms and then sends nothing for 12 seconds, three times running. It had
24
+ * been that way for eleven days, so every `nmts` command that needed the shard count on testnet
25
+ * simply stopped. Each host below answered `sui_getChainIdentifier` with the right value the
26
+ * same morning, 8/8 on a burst.
27
+ */
28
+ export declare const SUI_RPC_HOSTS: Readonly<Record<string, readonly string[]>>;
29
+ /** How long one host gets before the next is tried. A read that stalls is a read that failed. */
30
+ export declare const READ_TIMEOUT_MS = 60000;
31
+ /**
32
+ * Point reads at somebody else's aggregator, or at a development stack.
33
+ *
34
+ * ⚠ It replaces the list rather than adding to it, and that is deliberate: a run should read from
35
+ * where it was told to read, not from there AND the public hosts. Comma-separated for more
36
+ * than one, tried in the order given.
37
+ */
38
+ export declare const AGGREGATOR_ENV_VAR = "NMTS_AGGREGATOR";
39
+ /**
40
+ * Push writes through somebody else's relay, or through a development stack.
41
+ *
42
+ * ⚠ ONE host, not a list. Unlike reads there is nothing to fail over to — see `RELAY_HOSTS`.
43
+ */
44
+ export declare const RELAY_ENV_VAR = "NMTS_RELAY";
45
+ /** Ask a different Sui JSON-RPC node the shard-count question. */
46
+ export declare const SUI_RPC_ENV_VAR = "NMTS_SUI_RPC";
47
+ /** The relay this run writes through: the environment's if it named one, else the network's. */
48
+ export declare function relayHost(network: string): string;
49
+ /**
50
+ * Every Sui JSON-RPC node this run may ask, in order.
51
+ *
52
+ * ⛔ Naming one in the environment REPLACES the list rather than adding to it — the same rule the
53
+ * aggregator override follows, and for the same reason: somebody who names a node is saying
54
+ * *that one*, and quietly reaching a public mirror instead would send their traffic somewhere
55
+ * they did not choose.
56
+ */
57
+ export declare function suiRpcHosts(network: string): readonly string[];
58
+ /** The node whose address gets RECORDED — the first one, since that is the one normally asked. */
59
+ export declare function suiRpcHost(network: string): string;
60
+ export interface ReadOptions {
61
+ /** Override the host list — for a development stack, or an aggregator somebody runs themselves. */
62
+ hosts?: readonly string[];
63
+ timeoutMs?: number;
64
+ signal?: AbortSignal | undefined;
65
+ /**
66
+ * Read only these bytes: inclusive start, EXCLUSIVE end. Absent reads the whole object.
67
+ *
68
+ * ⛔ IT IS A REQUEST, NOT A GUARANTEE. An aggregator is free to ignore `Range` and answer 200
69
+ * with everything, so what comes back is cut to the asked-for length here. Without that cut
70
+ * the one caller that uses this — the rebuild's 72-byte key check — would quietly become a
71
+ * download of the whole account.
72
+ */
73
+ range?: {
74
+ start: number;
75
+ end: number;
76
+ };
77
+ }
78
+ /** Whole-blob read: `GET {aggregator}/v1/blobs/{blobId}`. */
79
+ export declare function readBlob(network: string, blobId: string, options?: ReadOptions): Promise<Uint8Array>;
80
+ /**
81
+ * Quilt-patch read: `GET {aggregator}/v1/blobs/by-quilt-patch-id/{patchId}`.
82
+ *
83
+ * A quilt is one stored blob holding many small files; the patch id addresses one of them. Files
84
+ * under 64 MiB share a quilt, so this is the common path rather than the exotic one.
85
+ */
86
+ export declare function readQuiltPatch(network: string, patchId: string, options?: ReadOptions): Promise<Uint8Array>;
package/dist/walrus.js CHANGED
@@ -36,10 +36,17 @@ export const RELAY_HOSTS = {
36
36
  * the storage network currently has, which the erasure coding needs. A wrong answer produces a
37
37
  * blob id the storage network refuses, so it fails loudly at the relay rather than quietly
38
38
  * storing something unreadable. No key is ever sent to one of these, and nothing is signed.
39
+ *
40
+ * ⭐ 2026-09-01 — a LIST per network, two operators, first-that-answers. It used to be one host,
41
+ * and the testnet one was measured dead that morning: `rpc-testnet.suiscan.xyz` completes the
42
+ * TCP handshake in 31 ms and then sends nothing for 12 seconds, three times running. It had
43
+ * been that way for eleven days, so every `nmts` command that needed the shard count on testnet
44
+ * simply stopped. Each host below answered `sui_getChainIdentifier` with the right value the
45
+ * same morning, 8/8 on a burst.
39
46
  */
40
47
  export const SUI_RPC_HOSTS = {
41
- testnet: "https://rpc-testnet.suiscan.xyz",
42
- mainnet: "https://rpc-mainnet.suiscan.xyz",
48
+ testnet: ["https://sui-testnet-rpc.publicnode.com", "https://testnet.suiet.app"],
49
+ mainnet: ["https://rpc-mainnet.suiscan.xyz", "https://sui-rpc.publicnode.com"],
43
50
  };
44
51
  /** How long one host gets before the next is tried. A read that stalls is a read that failed. */
45
52
  export const READ_TIMEOUT_MS = 60_000;
@@ -72,18 +79,29 @@ export function relayHost(network) {
72
79
  }
73
80
  return host;
74
81
  }
75
- /** The Sui JSON-RPC node this run asks about shard count. */
76
- export function suiRpcHost(network) {
82
+ /**
83
+ * Every Sui JSON-RPC node this run may ask, in order.
84
+ *
85
+ * ⛔ Naming one in the environment REPLACES the list rather than adding to it — the same rule the
86
+ * aggregator override follows, and for the same reason: somebody who names a node is saying
87
+ * *that one*, and quietly reaching a public mirror instead would send their traffic somewhere
88
+ * they did not choose.
89
+ */
90
+ export function suiRpcHosts(network) {
77
91
  const named = process.env[SUI_RPC_ENV_VAR]?.trim();
78
92
  if (named)
79
- return named;
80
- const host = SUI_RPC_HOSTS[network];
81
- if (host === undefined) {
93
+ return [named];
94
+ const hosts = SUI_RPC_HOSTS[network];
95
+ if (hosts === undefined || hosts.length === 0) {
82
96
  throw new NmtsError(`No Sui RPC endpoint is known for the ${network} network.`, {
83
97
  nextStep: `Name one in ${SUI_RPC_ENV_VAR} to upload anyway.`,
84
98
  });
85
99
  }
86
- return host;
100
+ return hosts;
101
+ }
102
+ /** The node whose address gets RECORDED — the first one, since that is the one normally asked. */
103
+ export function suiRpcHost(network) {
104
+ return suiRpcHosts(network)[0] ?? "";
87
105
  }
88
106
  function fromEnvironment() {
89
107
  const raw = process.env[AGGREGATOR_ENV_VAR];
@@ -122,12 +140,24 @@ async function readFrom(network, pathOf, what, options) {
122
140
  const timer = AbortSignal.timeout(options.timeoutMs ?? READ_TIMEOUT_MS);
123
141
  const signal = options.signal === undefined ? timer : AbortSignal.any([timer, options.signal]);
124
142
  try {
125
- const response = await fetch(url, { signal, redirect: "follow" });
143
+ const response = await fetch(url, { signal, redirect: "follow", ...headersFor(options) });
126
144
  if (!response.ok) {
127
145
  tried.push(`${host} → ${response.status}`);
128
146
  continue;
129
147
  }
130
- return new Uint8Array(await response.arrayBuffer());
148
+ const body = new Uint8Array(await response.arrayBuffer());
149
+ const range = options.range;
150
+ if (range === undefined)
151
+ return body;
152
+ const want = range.end - range.start;
153
+ // A 206 already holds exactly the asked-for bytes; a 200 holds the object from byte zero.
154
+ if (body.length === want)
155
+ return body;
156
+ if (body.length < range.end) {
157
+ tried.push(`${host} → answered ${body.length} bytes for a ${want}-byte range`);
158
+ continue;
159
+ }
160
+ return body.subarray(range.start, range.end);
131
161
  }
132
162
  catch (error) {
133
163
  // ⛔ The reason is kept, not flattened to "failed". A timeout and a refused connection mean
@@ -141,6 +171,14 @@ async function readFrom(network, pathOf, what, options) {
141
171
  `this is the wrong network — the same identifier does not exist on both.`,
142
172
  });
143
173
  }
174
+ /** The `Range` header for a partial read, or nothing at all for a whole one. */
175
+ function headersFor(options) {
176
+ const range = options.range;
177
+ if (range === undefined)
178
+ return {};
179
+ // HTTP ranges are inclusive at both ends; this one is exclusive at the end.
180
+ return { headers: { Range: `bytes=${range.start}-${range.end - 1}` } };
181
+ }
144
182
  /** Whole-blob read: `GET {aggregator}/v1/blobs/{blobId}`. */
145
183
  export function readBlob(network, blobId, options = {}) {
146
184
  return readFrom(network, (host) => `${host}/v1/blobs/${encodeURIComponent(blobId)}`, `Blob ${blobId}`, options);
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Make this program able to write and read compression flag 0x02. Safe to call more than once.
3
+ *
4
+ * ⛔ CALLED FROM THE FILE-LIST FLOW RATHER THAN FROM THE ENTRY POINT, and that is deliberate: the
5
+ * package is a library and an MCP server as well as a command, and a list opened through any of
6
+ * those doors has to be able to expand a zstd frame the browser wrote. Putting it here also
7
+ * means `nmts --help` never loads it.
8
+ */
9
+ export declare function registerNodeZstd(): void;
10
+ /** For tests that need this process to look like a build with no encoder. */
11
+ export declare function forgetNodeZstd(): void;