@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,232 @@
1
+ // `nmts create` without a key: the code is made here, and a PERSON finishes the account in a
2
+ // browser.
3
+ //
4
+ // ⛔ WHY THIS PATH EXISTS. The other one needs an API key whose account had a person pass the
5
+ // human check — which is fine once somebody has an account, and impossible for the first one.
6
+ // So the tool makes the NMTS key on this machine, buys a one-time address with a proof of
7
+ // work, and prints it. A person opens that address, types the code this printed, passes the
8
+ // check, and the account is made at that moment. Nothing here can do it for them, and that is
9
+ // the point rather than a limitation.
10
+ //
11
+ // ⛔ THE CODE NEVER GOES TO THE SERVER, NOT EVEN HASHED. What is sent is the account id — a public
12
+ // identifier derived one-way from the code — and nothing else. The pair that proves ownership
13
+ // is derived IN THE BROWSER from what the person types there.
14
+ //
15
+ // ⛔ THE FILE IS WRITTEN BEFORE THE ADDRESS IS ASKED FOR, the same rule the key path keeps: a full
16
+ // disk must fail while there is nothing to lose. ⚠ Unlike that path, a refusal afterwards does
17
+ // NOT remove the file — nothing has been created yet, so the code is not "the key to an account
18
+ // that may exist"; it is simply a code nobody has registered, and running again makes another.
19
+ // Removing it would be deleting a file a person may already have copied somewhere.
20
+ //
21
+ // ⛔ NOTHING IS STORED ON THIS MACHINE. `nmts login` is what puts a code here, and it is a separate
22
+ // act on purpose — see the header of `create.ts`.
23
+ import { createHash } from "node:crypto";
24
+ import { request } from "../api.js";
25
+ import { NmtsError } from "../errors.js";
26
+ import { isRecord } from "../guards.js";
27
+ import { BINARY_NAME, HOME_URL } from "../product.js";
28
+ import { newAccountCode, registrationProofOf } from "../registration.js";
29
+ import { codeFileTarget, jsonNeedsAFile, writeCodeFile } from "./create-code-file.js";
30
+ /** How often the status of a link is asked about, in milliseconds. */
31
+ const POLL_EVERY_MS = 3_000;
32
+ /** How long this waits before giving up on a person, in milliseconds. Matches the link's own life. */
33
+ const WAIT_FOR_MS = 30 * 60 * 1000;
34
+ /** Say "this is working" only if the work actually takes a moment. */
35
+ const QUIET_FOR_MS = 1_000;
36
+ /** Hashes between clock reads while solving. Small enough to notice a second; large enough to cost nothing. */
37
+ const CHECK_EVERY = 4_096;
38
+ export async function createThroughLink(options) {
39
+ const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
40
+ const { server, network } = options;
41
+ // ⛔ EVERY REFUSAL THAT DOES NOT NEED THE NETWORK HAPPENS FIRST.
42
+ const codeFile = codeFileTarget(options.out);
43
+ if (options.json === true && codeFile === null)
44
+ throw jsonNeedsAFile();
45
+ const puzzle = await askForWork(server);
46
+ const code = await newAccountCode();
47
+ const proof = await registrationProofOf(code);
48
+ if (codeFile !== null)
49
+ writeCodeFile(codeFile, code);
50
+ const nonce = solve(puzzle.challenge, proof.accountId, puzzle.bits, say);
51
+ const answer = await request(server, "/v1/accounts/registration-links", {
52
+ method: "POST",
53
+ body: { account_id: proof.accountId, challenge: puzzle.challenge, nonce },
54
+ });
55
+ const url = stringField(answer, "url");
56
+ const expiresAt = stringField(answer, "expires_at");
57
+ if (url === null)
58
+ throw unexpected("an address");
59
+ const linkId = url.slice(url.lastIndexOf("/") + 1);
60
+ const statusUrl = `${server}/v1/accounts/registration-links/${linkId}`;
61
+ if (options.json === true && codeFile !== null) {
62
+ // ⛔ NO `account_code` FIELD, AND THERE NEVER WILL BE — see `create.ts`. ⚠ And no waiting:
63
+ // machine-readable output is one object, and the address is worth nothing to the program
64
+ // reading it until it has handed it to a person. `status_url` is how it finds out what
65
+ // happened next.
66
+ say(JSON.stringify({ url, expires_at: expiresAt, code_file: codeFile, status_url: statusUrl }));
67
+ return 0;
68
+ }
69
+ sayTheAddress(say, url, expiresAt, server, network);
70
+ await sayTheQr(say, url);
71
+ if (options.noWait === true) {
72
+ say(``);
73
+ say(`Not waiting. To find out whether it was used:`);
74
+ say(``);
75
+ say(` ${statusUrl}`);
76
+ say(``);
77
+ say(`It answers {"status":"pending"}, {"status":"done"} or {"status":"expired"}.`);
78
+ sayWhereTheCodeIs(say, codeFile, code);
79
+ return 0;
80
+ }
81
+ say(``);
82
+ say(`Waiting. This stops on its own when the address runs out.`);
83
+ const finished = await waitForAPerson(server, linkId);
84
+ if (!finished)
85
+ throw nobodyCame(statusUrl);
86
+ say(``);
87
+ say(`Registered. The account exists now.`);
88
+ sayWhereTheCodeIs(say, codeFile, code);
89
+ sayWhatIsNext(say);
90
+ return 0;
91
+ }
92
+ async function askForWork(server) {
93
+ const answer = await request(server, "/v1/accounts/registration-challenge");
94
+ const challenge = stringField(answer, "challenge");
95
+ const bits = isRecord(answer) ? answer["difficulty_bits"] : null;
96
+ if (challenge === null || typeof bits !== "number" || !Number.isFinite(bits) || bits < 0) {
97
+ throw unexpected("a challenge");
98
+ }
99
+ return { challenge, bits };
100
+ }
101
+ /**
102
+ * Find a nonce whose digest under this challenge starts with `bits` zero bits.
103
+ *
104
+ * ⛔ THE RULE IS `SHA-256(challenge ‖ account_id ‖ nonce)` OVER THE THREE STRINGS' UTF-8 BYTES, IN
105
+ * THAT ORDER, WITH NOTHING BETWEEN THEM. The server computes the same digest and refuses
106
+ * anything else, so this sentence is the contract between the two — a separator nobody wrote
107
+ * down is how two implementations of one rule stop agreeing. The account id is in it so that
108
+ * one piece of work buys an address for one id and nothing else.
109
+ *
110
+ * ⚠ IT BLOCKS. At the difficulty this service sets it is about a million digests and well under a
111
+ * couple of seconds; there is nothing else for this command to be doing meanwhile, and a
112
+ * progress bar over a number nobody can act on is noise. One line, and only if it is slow.
113
+ */
114
+ function solve(challenge, accountId, bits, say) {
115
+ const started = Date.now();
116
+ let said = false;
117
+ for (let n = 0;; n += 1) {
118
+ const nonce = String(n);
119
+ const digest = createHash("sha256").update(challenge).update(accountId).update(nonce).digest();
120
+ if (leadingZeroBits(digest) >= bits) {
121
+ return nonce;
122
+ }
123
+ if (n % CHECK_EVERY === 0 && !said && Date.now() - started > QUIET_FOR_MS) {
124
+ say(`Preparing…`);
125
+ said = true;
126
+ }
127
+ }
128
+ }
129
+ function leadingZeroBits(digest) {
130
+ let seen = 0;
131
+ for (const byte of digest) {
132
+ seen += Math.clz32(byte) - 24;
133
+ if (byte !== 0)
134
+ break;
135
+ }
136
+ return seen;
137
+ }
138
+ /**
139
+ * Ask the server, every few seconds, whether somebody has used the address.
140
+ *
141
+ * ⚠ `expired` ENDS THE WAIT. The link's life and this deadline are the same thirty minutes, so a
142
+ * server that says "expired" is telling us the person did not come — waiting past that would be
143
+ * waiting for something that can no longer happen.
144
+ */
145
+ async function waitForAPerson(server, linkId) {
146
+ const deadline = Date.now() + WAIT_FOR_MS;
147
+ for (;;) {
148
+ const answer = await request(server, `/v1/accounts/registration-links/${linkId}`);
149
+ const status = stringField(answer, "status");
150
+ if (status === "done")
151
+ return true;
152
+ if (status === "expired")
153
+ return false;
154
+ if (Date.now() >= deadline)
155
+ return false;
156
+ await new Promise((done) => setTimeout(done, POLL_EVERY_MS));
157
+ }
158
+ }
159
+ function sayTheAddress(say, url, expiresAt, server, network) {
160
+ say(`A person has to finish this in a browser. Nothing on this machine can.`);
161
+ say(``);
162
+ say(` ${url}`);
163
+ say(``);
164
+ say(expiresAt === null
165
+ ? `Opening it asks for the NMTS key below, and for the check that says a person is here.`
166
+ : `It works until ${expiresAt}. Opening it asks for the NMTS key below, and for the ` +
167
+ `check that says a person is here.`);
168
+ say(`The account will exist on ${server} (${network}) the moment they finish.`);
169
+ }
170
+ /**
171
+ * The address again, as something a phone can read.
172
+ *
173
+ * ⚠ The same settings the browser's own codes use (`wallet address --qr`): medium error correction
174
+ * and a two-module quiet zone, without which many scanners see nothing at all.
175
+ */
176
+ async function sayTheQr(say, url) {
177
+ const { renderUnicodeCompact } = await import("uqr");
178
+ say(``);
179
+ for (const row of renderUnicodeCompact(url, { ecc: "M", border: 2 }).split("\n"))
180
+ say(` ${row}`);
181
+ }
182
+ /**
183
+ * The code, last, with the one thing that has to be understood before it.
184
+ *
185
+ * ⛔ NOTHING FOLLOWS IT BUT WHAT TO DO WITH IT. A screen of next steps under an NMTS key is how
186
+ * a person scrolls past the only copy of it.
187
+ */
188
+ function sayWhereTheCodeIs(say, codeFile, code) {
189
+ say(``);
190
+ say(`⛔ THIS IS THE ONLY COPY OF THE NMTS KEY THAT WILL EVER EXIST.`);
191
+ say(` NMTS keeps a verifier and never the NMTS key. It cannot be reset, resent or replaced.`);
192
+ say(` Lose it and the account and every file in it are gone — for you and for NMTS.`);
193
+ say(``);
194
+ say(` ${codeFile ?? code}`);
195
+ say(``);
196
+ say(codeFile === null
197
+ ? `It is what the browser asks for, and what \`${BINARY_NAME} login\` stores on a machine.`
198
+ : `That file holds it, readable by you alone. It is what the browser asks for.`);
199
+ }
200
+ /**
201
+ * The key is what makes the server answer this tool, and since 2026-09-05 the NMTS key this
202
+ * machine already holds is enough to make one — no browser, no person at the account screen.
203
+ */
204
+ function sayWhatIsNext(say) {
205
+ say(``);
206
+ say(`Next: \`${BINARY_NAME} key new\` makes this machine's API key out of that NMTS key and stores it.`);
207
+ say(`Nothing else is needed before the first \`${BINARY_NAME} put\`.`);
208
+ }
209
+ function nobodyCame(statusUrl) {
210
+ return new NmtsError(`The address ran out before anybody used it.`, {
211
+ // ⛔ 5 — "waiting on the person". Nothing went wrong and nothing is malformed; what is missing
212
+ // is an act only a person can perform.
213
+ exitCode: 5,
214
+ nextStep: [
215
+ `No account was created, and the NMTS key that was made for it is now good for nothing.`,
216
+ `Running \`${BINARY_NAME} create\` again makes a NEW NMTS key and a NEW address; it does not`,
217
+ `resume this one.`,
218
+ ``,
219
+ `If somebody is finishing it right now, this says so: ${statusUrl}`,
220
+ ].join("\n"),
221
+ });
222
+ }
223
+ function unexpected(what) {
224
+ return new NmtsError(`The server did not answer with ${what}.`, {
225
+ exitCode: 1,
226
+ nextStep: `Nothing was created. This tool may be older than the server it is talking to.`,
227
+ });
228
+ }
229
+ function stringField(answer, field) {
230
+ const value = isRecord(answer) ? answer[field] : null;
231
+ return typeof value === "string" && value.length > 0 ? value : null;
232
+ }
@@ -0,0 +1,20 @@
1
+ export interface CreateOptions {
2
+ server?: string | undefined;
3
+ network?: string | undefined;
4
+ json?: boolean;
5
+ /** A file to write the new NMTS key into, instead of printing it. Never overwritten. */
6
+ out?: string | undefined;
7
+ /** The Terms of Service version a PERSON read and accepts for the new account. */
8
+ acceptTerms?: string | undefined;
9
+ /** The Privacy Policy version accepted in the same act. */
10
+ acceptPrivacy?: string | undefined;
11
+ /**
12
+ * Link path only: print the address and stop, instead of waiting for a person to use it.
13
+ *
14
+ * ⚠ IT DOES NOTHING ON THE KEY PATH, because there is nothing to wait for there — the account
15
+ * exists by the time that path prints anything.
16
+ */
17
+ noWait?: boolean | undefined;
18
+ write?: (line: string) => void;
19
+ }
20
+ export declare function create(options?: CreateOptions): Promise<number>;
@@ -16,7 +16,7 @@
16
16
  //
17
17
  // ⛔ WITH `--json` THE CODE DOES NOT GO INTO THE OUTPUT. Machine-readable output is read by a
18
18
  // program, which means it lands in a pipe, a file, a CI log or an agent's transcript — the
19
- // exact places `credentials.ts` spends its whole header keeping the account code out of. So
19
+ // exact places `credentials.ts` spends its whole header keeping the NMTS key out of. So
20
20
  // `--json` is refused unless `--out <file>` names somewhere for the code to go, and the JSON
21
21
  // then carries the PATH and not the value. That is the same shape this tool already recommends
22
22
  // for handing a secret to a container (`NMTS_ACCOUNT_CODE_FILE` — a variable holding a
@@ -29,38 +29,56 @@
29
29
  // server has created the account would mean an account exists whose only key we are about to
30
30
  // drop. If the creation then fails, the file is removed again — no account, no code, no trace.
31
31
  //
32
- // THE FIRST DOOR OF `POST /v1/accounts` IS NOT AVAILABLE TO THIS TOOL AND NEVER WILL BE. The
33
- // server takes either a solved human check with no credential, or an API key whose account had
34
- // a PERSON pass that check within four of its weeks. A machine cannot solve the first, so this
35
- // command works only for a caller that already has an account and a verified key and it says
36
- // that plainly, before it makes anything, rather than failing with a message about scopes.
37
- import { mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
38
- import { isAbsolute, resolve } from "node:path";
32
+ // ⛔⭐ THERE ARE TWO PATHS, AND WHICH ONE RUNS IS DECIDED BY WHAT THIS MACHINE HOLDS (2026-09-05).
33
+ // `POST /v1/accounts` takes either a solved human check which no machine can produce
34
+ // — or an API key whose account had a PERSON pass that check within four of its weeks. That
35
+ // second door is this file, and it is only open to a caller that ALREADY has an account.
36
+ // With no such key, the command does not refuse any more: it makes the code here, buys a
37
+ // one-time address with a proof of work, and prints it for a PERSON to open (`create-link.ts`).
38
+ // The person types the code in a browser and the account is made there. ⚠ The person is not
39
+ // optional in either path — what changes is whether they are being asked to pass a check for an
40
+ // account they already have, or to finish one that does not exist yet.
41
+ import { rmSync } from "node:fs";
39
42
  import { request, ServerError } from "../api.js";
40
- import { readCredentialsFile } from "../credentials.js";
43
+ import { readCredentialsFile, resolveApiKey } from "../credentials.js";
41
44
  import { NmtsError } from "../errors.js";
42
45
  import { isRecord } from "../guards.js";
43
- import { askAPersonToVerify, humanCheck } from "../human-check.js";
46
+ import { humanCheck } from "../human-check.js";
44
47
  import { resolveNetwork } from "../network.js";
45
48
  import { BINARY_NAME, HOME_URL } from "../product.js";
46
49
  import { newAccountCode, registrationProofOf } from "../registration.js";
47
50
  import { resolveServer } from "../server.js";
48
- import { requireApiKey } from "../session.js";
49
- import { STDOUT_TARGET } from "../stdout.js";
51
+ import { codeFileTarget, jsonNeedsAFile, writeCodeFile } from "./create-code-file.js";
50
52
  export async function create(options = {}) {
51
53
  const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
52
- const apiKey = requireApiKey();
54
+ const held = resolveApiKey();
53
55
  const stored = readCredentialsFile();
54
56
  const server = resolveServer(options.server ?? stored?.server);
55
57
  const network = resolveNetwork(server, options.network ?? stored?.network);
58
+ // ⛔ THE KEY PATH IS TAKEN ONLY WHEN IT WOULD ACTUALLY WORK. A key whose account has no live
59
+ // human check cannot create anything (the server refuses it), and refusing here instead
60
+ // would leave the caller with no way to make a first account at all — which is the wall this
61
+ // tool used to end at. ⚠ A key that is revoked or unreadable still fails LOUDLY: `humanCheck`
62
+ // throws for those, and falling through to the other path would hide a broken credential
63
+ // behind a flow that happens to work.
64
+ const check = held === null ? null : await humanCheck(server, held.key);
65
+ if (held === null || check === null || !check.live) {
66
+ const { createThroughLink } = await import("./create-link.js");
67
+ return await createThroughLink({
68
+ server,
69
+ network,
70
+ out: options.out,
71
+ json: options.json === true,
72
+ noWait: options.noWait,
73
+ write: options.write,
74
+ });
75
+ }
76
+ const apiKey = held.key;
56
77
  // ⛔ EVERY REFUSAL THAT DOES NOT NEED THE NETWORK HAPPENS FIRST, so a run that was never going
57
78
  // to work does not spend one of the creator's two accounts for the day finding that out.
58
79
  const codeFile = codeFileTarget(options.out);
59
80
  if (options.json === true && codeFile === null)
60
81
  throw jsonNeedsAFile();
61
- const check = await humanCheck(server, apiKey);
62
- if (!check.live)
63
- throw askAPersonToVerify("No account can be created");
64
82
  const inForce = await termsInForce(server, apiKey);
65
83
  const accepted = inForce === null ? null : acceptanceOffered(inForce, options);
66
84
  const code = await newAccountCode();
@@ -114,74 +132,6 @@ export async function create(options = {}) {
114
132
  sayWhereTheCodeWent(say, codeFile);
115
133
  return 0;
116
134
  }
117
- /**
118
- * Where the code goes, or `null` for the screen.
119
- *
120
- * ⛔ `-` IS REFUSED, WHICH IS THE OPPOSITE OF WHAT IT MEANS EVERYWHERE ELSE IN THIS TOOL. In
121
- * `get` and `listfile` it means "hand the bytes to whatever is reading stdout", and that is
122
- * right for a file somebody already has. Here it would mean putting the only copy of an
123
- * account code into the same stream a program is parsing — which is the one place this command
124
- * exists to keep it out of.
125
- */
126
- function codeFileTarget(out) {
127
- if (out === undefined || out === "")
128
- return null;
129
- if (out === STDOUT_TARGET) {
130
- throw new NmtsError("The new account code will not be sent to stdout.", {
131
- exitCode: 2,
132
- nextStep: `Nothing was created. stdout is what a program reads and a log keeps, and this is the ` +
133
- `only copy of the code. Name a file — \`--out ./account-code.txt\` — or leave --out off ` +
134
- `and read it off the screen.`,
135
- });
136
- }
137
- const path = isAbsolute(out) ? out : resolve(process.cwd(), out);
138
- let existing = null;
139
- try {
140
- existing = statSync(path);
141
- }
142
- catch {
143
- // Not there is exactly what this wants.
144
- }
145
- if (existing !== null) {
146
- // ⛔ NO `--force` HERE, DELIBERATELY. Everywhere else in this tool --force replaces a file
147
- // that can be fetched again. The file this would replace may be the only copy of ANOTHER
148
- // account's code, and overwriting it destroys that account with no way back.
149
- throw new NmtsError(`${path} is already there.`, {
150
- exitCode: 4,
151
- nextStep: existing.isDirectory()
152
- ? `--out names the FILE the code goes into, not a directory.`
153
- : `Nothing was created. That file is not replaced, whatever --force says: it may hold ` +
154
- `the only copy of another account's code. Name one that does not exist.`,
155
- });
156
- }
157
- return path;
158
- }
159
- /**
160
- * Write the code where the caller pointed, readable by nobody else.
161
- *
162
- * ⚠ `wx` FAILS IF THE NAME APPEARED SINCE THE CHECK ABOVE, which is the point of using it rather
163
- * than trusting that check: between the two, something else may have written there.
164
- *
165
- * ⚠ ON WINDOWS THE MODE IS IGNORED and the file inherits the folder's permissions — the same
166
- * limit `credentials.ts` documents, and claiming otherwise would be claiming a guarantee the
167
- * platform does not give.
168
- */
169
- function writeCodeFile(path, code) {
170
- const dir = resolve(path, "..");
171
- mkdirSync(dir, { recursive: true, mode: 0o700 });
172
- try {
173
- writeFileSync(path, `${code}\n`, { mode: 0o600, flag: "wx" });
174
- }
175
- catch (error) {
176
- // ⛔ THE CAUSE IS NAMED BUT THE CODE IS NOT. `writeFileSync`'s errno line carries the path and
177
- // never the contents, so it is safe to pass on; the code itself appears in no message here.
178
- throw new NmtsError(`The account code could not be written to ${path}.`, {
179
- exitCode: 1,
180
- nextStep: `Nothing was created — the file is written before the account is asked for, so that a ` +
181
- `failure here costs nothing. Cause: ${error instanceof Error ? error.message : String(error)}`,
182
- });
183
- }
184
- }
185
135
  /** What documents this server is enforcing, read from the one route a key may ask. */
186
136
  async function termsInForce(server, apiKey) {
187
137
  const answer = await request(server, "/v1/account/summary", { token: apiKey });
@@ -228,7 +178,7 @@ function termsRefusal(inForce, named) {
228
178
  // command line is not malformed. What is missing is a decision only a person can take.
229
179
  exitCode: 5,
230
180
  nextStep: [
231
- `Nothing was created and no account code was made.`,
181
+ `Nothing was created and no NMTS key was made.`,
232
182
  ``,
233
183
  `In force now:`,
234
184
  ` Terms of Service ${inForce.terms}`,
@@ -293,7 +243,7 @@ function uncertain(path, cause) {
293
243
  return new NmtsError(`The server did not answer, so whether the account was created is not known here.`, {
294
244
  exitCode: 1,
295
245
  nextStep: [
296
- `${path} was KEPT, and it holds the only code that account would have.`,
246
+ `${path} was KEPT, and it holds the only NMTS key that account would have.`,
297
247
  ``,
298
248
  `Keep it until you know. Nothing on this machine can tell the two cases apart, and the`,
299
249
  `cost of being wrong is one small file against an account nobody could ever open.`,
@@ -303,15 +253,6 @@ function uncertain(path, cause) {
303
253
  ].join("\n"),
304
254
  });
305
255
  }
306
- function jsonNeedsAFile() {
307
- return new NmtsError("--json needs --out, because the account code will not go into the output.", {
308
- exitCode: 2,
309
- nextStep: `Nothing was created. Machine-readable output is read by a program and kept by a log, and ` +
310
- `the code is the only key this account will ever have. \`--out ./account-code.txt\` writes ` +
311
- `it to a file only you can read; the JSON then names that file. Without --json the code is ` +
312
- `printed on the screen for a person to keep.`,
313
- });
314
- }
315
256
  /** One field of the answer's `account` object, or null. The server's shape, not ours. */
316
257
  function accountField(answer, field) {
317
258
  const account = isRecord(answer) ? answer["account"] : null;
@@ -325,19 +266,19 @@ function sayCreated(say, accountId, server, network) {
325
266
  say(``);
326
267
  say(` account id ${accountId}`);
327
268
  say(``);
328
- say(`Nothing on this machine changed. The account code you were signed in as is still the one`);
269
+ say(`Nothing on this machine changed. The NMTS key you were signed in as is still the one`);
329
270
  say(`this tool uses; this new account is not stored, and not switched to.`);
330
271
  say(``);
331
272
  }
332
273
  /**
333
274
  * The code, last, with the one thing that has to be understood before it.
334
275
  *
335
- * ⛔ NOTHING FOLLOWS IT BUT THE COMMAND THAT STORES IT. A screen of next steps under an account
336
- * code is how a person scrolls past the only copy of it.
276
+ * ⛔ NOTHING FOLLOWS IT BUT THE COMMAND THAT STORES IT. A screen of next steps under an NMTS
277
+ * key is how a person scrolls past the only copy of it.
337
278
  */
338
279
  function sayTheCode(say, code) {
339
- say(`⛔ THIS IS THE ONLY COPY OF THE ACCOUNT CODE THAT WILL EVER EXIST.`);
340
- say(` NMTS keeps a verifier and never the code. It cannot be reset, resent or replaced.`);
280
+ say(`⛔ THIS IS THE ONLY COPY OF THE NMTS KEY THAT WILL EVER EXIST.`);
281
+ say(` NMTS keeps a verifier and never the NMTS key. It cannot be reset, resent or replaced.`);
341
282
  say(` Lose it and the account and every file in it are gone — for you and for NMTS.`);
342
283
  say(``);
343
284
  say(` ${code}`);
@@ -345,8 +286,8 @@ function sayTheCode(say, code) {
345
286
  say(`To use this machine as that account: \`${BINARY_NAME} logout\`, then \`${BINARY_NAME} login\`.`);
346
287
  }
347
288
  function sayWhereTheCodeWent(say, path) {
348
- say(`⛔ THE ONLY COPY OF THE ACCOUNT CODE IS NOW IN ONE FILE, AND NOWHERE ELSE.`);
349
- say(` NMTS keeps a verifier and never the code. It cannot be reset, resent or replaced.`);
289
+ say(`⛔ THE ONLY COPY OF THE NMTS KEY IS NOW IN ONE FILE, AND NOWHERE ELSE.`);
290
+ say(` NMTS keeps a verifier and never the NMTS key. It cannot be reset, resent or replaced.`);
350
291
  say(` Lose that file and the account and every file in it are gone.`);
351
292
  say(``);
352
293
  say(` ${path}`);
@@ -0,0 +1,9 @@
1
+ export interface CreditsOptions {
2
+ server?: string | undefined;
3
+ network?: string | undefined;
4
+ json?: boolean;
5
+ /** `--to`: which account receives, base64url, as `nmts whoami` prints it for that account. */
6
+ to?: string | undefined;
7
+ write?: (line: string) => void;
8
+ }
9
+ export declare function credits(action: string | undefined, amount: string | undefined, options?: CreditsOptions): Promise<number>;
@@ -0,0 +1,110 @@
1
+ // `nmts credits transfer --to <account-id> <credits>` — move credits to another account of the
2
+ // same family.
3
+ //
4
+ // ⛔ THE FAMILY IS THE WHOLE RANGE, AND THIS COMMAND CANNOT WIDEN IT. A family is the account a
5
+ // person made and every account under it; the server refuses anything else and answers an
6
+ // account that does not exist exactly the same way, so nothing here can be used to find out
7
+ // which account identifiers are real. There is no price, no fee and no third party — this is
8
+ // not a market, and it does not reopen what "credits cannot be transferred" was written for.
9
+ //
10
+ // ⛔ WHY IT EXISTS. The free trial, the human check and the ceilings belong to the whole family:
11
+ // one week's place for the tree, not one each. Without a way to move credits that place lands
12
+ // on ONE account and the account that needs it cannot be given it.
13
+ //
14
+ // ⛔ IT ASKS ABOUT THE PERSON'S CHECK FIRST, for the reason `commands/trial.ts` sets out at
15
+ // length: the server decides scope before it looks at the check, so a caller can be told "the
16
+ // key was not given permission" when what is actually missing is a person — which sends an
17
+ // agent off making another key, the one remedy that cannot help.
18
+ //
19
+ // ⚠ NOTHING IS RETRIED HERE. Every refusal this can meet is decided by the request itself (name
20
+ // another account, send less, send some), and `api.ts` already carries the advice for each.
21
+ import { request } from "../api.js";
22
+ import { readCredentialsFile } from "../credentials.js";
23
+ import { NmtsError } from "../errors.js";
24
+ import { isRecord } from "../guards.js";
25
+ import { askAPersonToVerify, humanCheck } from "../human-check.js";
26
+ import { BINARY_NAME } from "../product.js";
27
+ import { resolveServer } from "../server.js";
28
+ import { requireApiKey } from "../session.js";
29
+ /** The one word this command takes. */
30
+ const TRANSFER = "transfer";
31
+ export async function credits(action, amount, options = {}) {
32
+ const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
33
+ if (action !== TRANSFER) {
34
+ throw new NmtsError(`\`${BINARY_NAME} credits\` takes \`${TRANSFER}\`, and nothing else.`, {
35
+ exitCode: 2,
36
+ nextStep: `\`${BINARY_NAME} credits ${TRANSFER} --to <account identifier> <credits>\` moves credits ` +
37
+ `to another account of your own. \`${BINARY_NAME} balance\` is what reads them.`,
38
+ });
39
+ }
40
+ // ⛔ BOTH OPERANDS ARE CHECKED BEFORE THE NETWORK. A missing recipient and an amount nobody
41
+ // could have meant are command lines to fix, not questions to ask the server — and an amount
42
+ // clamped into range here would move a number nobody typed.
43
+ const to = (options.to ?? "").trim();
44
+ if (to === "")
45
+ throw needsTo();
46
+ const moving = parseAmount(amount);
47
+ const apiKey = requireApiKey();
48
+ const stored = readCredentialsFile();
49
+ const server = resolveServer(options.server ?? stored?.server);
50
+ const check = await humanCheck(server, apiKey);
51
+ if (!check.live)
52
+ throw askAPersonToVerify("Credits cannot be moved");
53
+ const moved = read(await request(server, "/v1/credits/transfer", {
54
+ method: "POST",
55
+ token: apiKey,
56
+ body: { to, credits: moving },
57
+ }));
58
+ if (options.json === true) {
59
+ // The server's own spelling for its three numbers, plus the recipient this run named — so a
60
+ // reader that has seen the wire and then this is not converting between two sets of names.
61
+ say(JSON.stringify({ to, ...moved }));
62
+ return 0;
63
+ }
64
+ say(`Sent ${plural(moved.credits)} to ${to}.`);
65
+ say(``);
66
+ say(` this account ${plural(moved.from_balance)}`);
67
+ say(` that account ${plural(moved.to_balance)}`);
68
+ say(``);
69
+ say(` They keep the date they were already going to lapse on: moving credits does not renew`);
70
+ say(` them. \`${BINARY_NAME} balance\` reads the ledger of whichever account holds the key.`);
71
+ return 0;
72
+ }
73
+ function plural(n) {
74
+ return `${n} ${n === 1 ? "credit" : "credits"}`;
75
+ }
76
+ /** The amount, refused in this command's own words. ⚠ Whole and above zero: the server has its own
77
+ * code for a transfer of nothing, and this end can say so without spending a request on it. */
78
+ function parseAmount(raw) {
79
+ const n = raw === undefined ? Number.NaN : Number(raw);
80
+ if (!Number.isSafeInteger(n) || n < 1) {
81
+ throw new NmtsError(`\`${BINARY_NAME} credits ${TRANSFER}\` takes a whole number of credits above zero, not ` +
82
+ `"${raw ?? ""}".`, {
83
+ exitCode: 2,
84
+ nextStep: `\`${BINARY_NAME} credits ${TRANSFER} --to <account identifier> 5\` moves five. ` +
85
+ `\`${BINARY_NAME} balance\` says how many this account can spend.`,
86
+ });
87
+ }
88
+ return n;
89
+ }
90
+ function needsTo() {
91
+ return new NmtsError(`\`--to\` is required: it names the account that receives.`, {
92
+ exitCode: 2,
93
+ nextStep: `It takes an account identifier — what \`${BINARY_NAME} whoami\` prints for that account, ` +
94
+ `and what the account screen lists for an AI account. It has to be an account of your own ` +
95
+ `family: your account, or one made under it.`,
96
+ });
97
+ }
98
+ function read(answer) {
99
+ const credits = isRecord(answer) ? answer["credits"] : null;
100
+ const from = isRecord(answer) ? answer["from_balance"] : null;
101
+ const to = isRecord(answer) ? answer["to_balance"] : null;
102
+ if (typeof credits !== "number" || typeof from !== "number" || typeof to !== "number") {
103
+ throw new NmtsError("The server moved the credits and described it in a shape this version cannot read.", {
104
+ exitCode: 1,
105
+ nextStep: `The move happened whatever this printed — \`${BINARY_NAME} balance\` reads the ledger. ` +
106
+ `Update this tool.`,
107
+ });
108
+ }
109
+ return { credits, from_balance: from, to_balance: to };
110
+ }
@@ -0,0 +1,11 @@
1
+ /** The sentence the browser's erase dialog asks for — the same words, so the act is one act. */
2
+ export declare const CONFIRM_SENTENCE = "I UNDERSTAND THIS IS PERMANENT";
3
+ export interface DeleteAccountOptions {
4
+ server?: string | undefined;
5
+ write?: ((line: string) => void) | undefined;
6
+ /** Injected in tests: answers the one typed line. */
7
+ readLine?: ((question: string) => Promise<string>) | undefined;
8
+ /** Under skip-permissions only: the tier gate's answer, given with a `--reason`. */
9
+ yes?: boolean;
10
+ }
11
+ export declare function deleteAccount(options?: DeleteAccountOptions): Promise<number>;