@needmoretruth/nmts-cli 0.17.2 → 0.34.1

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 +124 -516
  2. package/CHANGELOG.md +98 -0
  3. package/README.ko.md +374 -467
  4. package/README.md +384 -465
  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,63 @@
1
+ // `nmts delete-account` — a PERSON erases this account's server record, from the terminal.
2
+ //
3
+ // ⛔ WHY IT EXISTS (2026-09-06 · CLI parity ③-3). Leaving is the one act the documents promise
4
+ // unconditionally, and an account driven only from a terminal had no door for it but a browser:
5
+ // the key door is shut to a bare key, and the sessionless erase door asks for a human check a
6
+ // program cannot pass. What opens the key door is the NMTS key's proof beside the key —
7
+ // the same proof a browser sign-in presents before its own erase button — so this command needs
8
+ // the code on this machine, is refused in mode auto before the code is opened, and has the person
9
+ // type the same sentence the browser asks for.
10
+ //
11
+ // ⛔ IT SAYS WHAT GOES AND WHAT STAYS, WORD FOR WORD WITH THE SERVER. Erased: the account row, the
12
+ // file list, every file's server record and share, the sessions and the keys. Not erased: the
13
+ // bytes on the storage network (paid through their term, and unreadable without the records),
14
+ // and the NMTS key — which stays on this machine until `nmts logout`, because a recovery
15
+ // list plus the code can still read what was stored. Not refunded: storage already paid for.
16
+ import { accountProofFor } from "../account-proof.js";
17
+ import { request } from "../api.js";
18
+ import { currentMode } from "../autonomy.js";
19
+ import { requireAccountCode } from "../code-access.js";
20
+ import { readCredentialsFile } from "../credentials.js";
21
+ import { NmtsError } from "../errors.js";
22
+ import { BINARY_NAME, HOME_URL } from "../product.js";
23
+ import { promptLine, stdinIsATerminal } from "../prompt.js";
24
+ import { resolveServer } from "../server.js";
25
+ import { requireApiKey } from "../session.js";
26
+ /** The sentence the browser's erase dialog asks for — the same words, so the act is one act. */
27
+ export const CONFIRM_SENTENCE = "I UNDERSTAND THIS IS PERMANENT";
28
+ export async function deleteAccount(options = {}) {
29
+ const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
30
+ // The tier gate has already refused the auto modes and, under skip-permissions, taken a
31
+ // --reason and the --yes. ⛔ THE SENTENCE IS STILL TYPED IN EVERY OTHER MODE: `--yes` alone does
32
+ // not stand for it — only the mode that turned every question off does.
33
+ const typedFor = options.yes === true && currentMode() === "skip-permissions";
34
+ const ask = options.readLine ?? promptLine;
35
+ if (!typedFor && options.readLine === undefined && !stdinIsATerminal()) {
36
+ throw new NmtsError("There is no terminal to type into (stdin is not a TTY).", {
37
+ exitCode: 3,
38
+ nextStep: `Run this where a person can type: the erasure is confirmed by typing a sentence.`,
39
+ });
40
+ }
41
+ const apiKey = requireApiKey();
42
+ const held = await requireAccountCode();
43
+ const stored = readCredentialsFile();
44
+ const server = resolveServer(options.server ?? stored?.server);
45
+ say(`This erases the account's server record. It cannot be undone.`);
46
+ say(` Erased: the account, the file list, every file's record and share, sessions and keys.`);
47
+ say(` Not erased: the bytes on the storage network (paid through their term; unreadable without`);
48
+ say(` the records), and the NMTS key on this machine — \`${BINARY_NAME} logout\` removes it.`);
49
+ say(` Not refunded: storage already paid for. The funds went to the network, never to NMTS.`);
50
+ say(``);
51
+ const typed = typedFor ? CONFIRM_SENTENCE : (await ask(`Type exactly: ${CONFIRM_SENTENCE}\n> `)).trim();
52
+ if (typed !== CONFIRM_SENTENCE) {
53
+ say(`Nothing was erased.`);
54
+ return 1;
55
+ }
56
+ // ⛔ THE PROOF IS BUILT FOR THIS ONE REQUEST AND NOTHING KEEPS IT.
57
+ const accountProof = await accountProofFor({ code: held.code, source: held.source });
58
+ await request(server, "/v1/account", { method: "DELETE", token: apiKey, accountProof });
59
+ say(`Erased. The server holds nothing about this account now.`);
60
+ say(`The stored NMTS key and API key on this machine are still here; \`${BINARY_NAME} logout\` removes them.`);
61
+ say(`A new account can be made at any time — \`${BINARY_NAME} create\` or ${HOME_URL}.`);
62
+ return 0;
63
+ }
@@ -0,0 +1,7 @@
1
+ export interface DepositOptions {
2
+ server?: string | undefined;
3
+ network?: string | undefined;
4
+ json?: boolean;
5
+ write?: (line: string) => void;
6
+ }
7
+ export declare function deposit(wanted: string | undefined, options?: DepositOptions): Promise<number>;
@@ -0,0 +1,85 @@
1
+ // `nmts deposit [<n>]` — how many credits this account sets aside on each credit-paid upload,
2
+ // when the upload itself does not say.
3
+ //
4
+ // ⛔ IT IS A DEFAULT, NOT A CHARGE. Nothing here moves a credit: it records the number `nmts put`
5
+ // and `nmts push` will send with the next upload. What the deposit then does is pay the chain
6
+ // fee of a later operation on that file, measured rather than spent whole, with the remainder
7
+ // returned when the storage period ends.
8
+ //
9
+ // ⛔ IT LIVES IN THE SEALED FILE LIST, NOT ON THIS MACHINE — the same place, and for the same
10
+ // reasons, as `nmts padding`: the server must not learn it (a per-account default is a small
11
+ // fingerprint a server could keep), and it follows the ACCOUNT, so a phone and a laptop set the
12
+ // same amount aside. That is why reading it costs a list read and setting it costs a list write.
13
+ //
14
+ // ⚠ AND IT APPLIES TO WHAT IS UPLOADED NEXT. A deposit already set aside on a stored file is that
15
+ // file's, and changing this number does not move it.
16
+ import { depositDefaultOf, DEPOSIT_CREDITS_DEFAULT, DEPOSIT_CREDITS_MAX, parseDeposit, } from "../deposit.js";
17
+ import { NmtsError } from "../errors.js";
18
+ import { readFileList } from "../manifest.js";
19
+ import { applyManyToList } from "../manifest-write.js";
20
+ import { BINARY_NAME } from "../product.js";
21
+ import { openSession } from "../session.js";
22
+ /** What a file with no deposit pays instead, said wherever 0 is on the screen. */
23
+ const WHAT_ZERO_COSTS = `A file uploaded with no deposit still releases: it pays twice the same chain fee out of your ` +
24
+ `balance at that moment, and is refused when the balance cannot cover it.`;
25
+ export async function deposit(wanted, options = {}) {
26
+ const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
27
+ // ⛔ BEFORE THE NETWORK. A number outside the range is a command line to fix, not a question to
28
+ // ask the server, and clamping it would record an amount nobody typed.
29
+ const asked = parseDepositArgument(wanted);
30
+ const session = await openSession({ server: options.server, network: options.network });
31
+ if (asked === undefined) {
32
+ const list = await readFileList(session.server, session.apiKey, session.code, session.accountId);
33
+ const held = list.manifest?.settings?.depositDefault;
34
+ const credits = depositDefaultOf(list.manifest?.settings);
35
+ const chosen = held !== undefined && credits === held;
36
+ if (options.json === true) {
37
+ say(JSON.stringify({ deposit: credits, set: chosen }));
38
+ return 0;
39
+ }
40
+ say(chosen ? `${credits}` : `${DEPOSIT_CREDITS_DEFAULT} (not set)`);
41
+ say(credits === 0
42
+ ? `Uploads paid for with credits set no deposit aside. ${WHAT_ZERO_COSTS}`
43
+ : `Uploads paid for with credits set ${credits} credit${credits === 1 ? "" : "s"} aside on ` +
44
+ `each file, back when that file's storage period ends. Anything from 0 to ` +
45
+ `${DEPOSIT_CREDITS_MAX} — \`${BINARY_NAME} deposit <n>\`, or --deposit for one upload.`);
46
+ return 0;
47
+ }
48
+ // ⛔ NO LIST, NO SETTING — the same refusal `padding` makes, for the same reason: the default
49
+ // lives inside the sealed list, and writing an empty one just to hold a setting would make a
50
+ // first `ls` say "a list exists" about an account nothing was ever put in.
51
+ const before = await readFileList(session.server, session.apiKey, session.code, session.accountId);
52
+ if (before.manifest === null) {
53
+ throw new NmtsError(`This account has no file list yet; the setting lives in the list, and there is nothing to write it into.`, { exitCode: 4, nextStep: `Upload once (\`${BINARY_NAME} put\`) and set it after.` });
54
+ }
55
+ const result = await applyManyToList(session, () => [], { depositDefault: asked });
56
+ if (options.json === true) {
57
+ say(JSON.stringify({ deposit: asked, set: true }));
58
+ return 0;
59
+ }
60
+ if (!result.changed) {
61
+ say(`Already ${asked}. Nothing changed.`);
62
+ return 0;
63
+ }
64
+ if (asked === 0) {
65
+ say(`Uploads from now on set no deposit aside. ${WHAT_ZERO_COSTS}`);
66
+ return 0;
67
+ }
68
+ say(`Set to ${asked}. It applies to what is uploaded next, from every device; deposits already ` +
69
+ `set aside on stored files are not moved.`);
70
+ return 0;
71
+ }
72
+ /** The operand, refused in this command's own words rather than the upload flag's. */
73
+ function parseDepositArgument(wanted) {
74
+ if (wanted === undefined || wanted === "")
75
+ return undefined;
76
+ try {
77
+ return parseDeposit(wanted);
78
+ }
79
+ catch {
80
+ throw new NmtsError(`\`${BINARY_NAME} deposit\` takes a whole number of credits from 0 to ${DEPOSIT_CREDITS_MAX}, not "${wanted}".`, {
81
+ exitCode: 2,
82
+ nextStep: `Run \`${BINARY_NAME} deposit\` with no argument to see what this account sets aside.`,
83
+ });
84
+ }
85
+ }
@@ -0,0 +1,12 @@
1
+ export interface DevicesOptions {
2
+ server?: string | undefined;
3
+ json?: boolean;
4
+ /** Sign ONE device out by id, or `all` of them. A high act: the tier gate asks first. */
5
+ signOut?: string | undefined;
6
+ /** The answer the tier gate already took, or the person's own `--yes`. */
7
+ yes?: boolean;
8
+ write?: (line: string) => void;
9
+ /** Injected in tests: answers the one confirmation. */
10
+ readLine?: ((question: string) => Promise<string>) | undefined;
11
+ }
12
+ export declare function devices(options?: DevicesOptions): Promise<number>;
@@ -0,0 +1,144 @@
1
+ // `nmts devices` — what is signed in to this account.
2
+ //
3
+ // ⛔ WHY IT EXISTS. An account run from a terminal could see everything about itself except who
4
+ // was holding a session to it. The browser has had that list since sessions had names; the one
5
+ // account nobody is watching a screen for had nothing. The server opened the READ to a key
6
+ // holding the files:read permission and left the two sign-outs shut, so this command lists and
7
+ // cannot end anything.
8
+ //
9
+ // ⛔ SIGNING A DEVICE OUT IS A PERSON'S ACT, AND `--sign-out` KEEPS IT ONE (2026-09-06 · CLI parity
10
+ // ③-2). A key alone must never end sessions: a stolen key that could end the browser's session
11
+ // could lock the person out of the one screen that revokes it, and the server still refuses a
12
+ // bare key. What opens the door is the NMTS key's proof beside the key — whoever holds the
13
+ // code can sign in and do the same from a browser — so this option needs the code on this
14
+ // machine, is refused in mode auto before the code is opened, and asks once before it acts.
15
+ //
16
+ // ⚠ THE DEVICE NAME IS SEALED AND STAYS SEALED HERE. It is encrypted under a key derived from the
17
+ // NMTS key, which the server has never had — so what a listing can honestly show is when a
18
+ // session started, when it was last used, and when it runs out. This command does not open the
19
+ // name: opening it needs the NMTS key, and this command runs on a key alone so that it
20
+ // works on a machine where the code is not present at all.
21
+ //
22
+ // ⚠ AND THE NETWORK IS NOT RESOLVED HERE. Nothing in this answer is stored on the storage
23
+ // network, so demanding to be told which one a development server uses would refuse a run over
24
+ // a fact it never consults — the same reading `listfile` and `env` already take.
25
+ import { accountProofFor } from "../account-proof.js";
26
+ import { request } from "../api.js";
27
+ import { requireAccountCode } from "../code-access.js";
28
+ import { readCredentialsFile } from "../credentials.js";
29
+ import { NmtsError } from "../errors.js";
30
+ import { isRecord } from "../guards.js";
31
+ import { BINARY_NAME, HOME_URL } from "../product.js";
32
+ import { promptLine, stdinIsATerminal } from "../prompt.js";
33
+ import { resolveServer } from "../server.js";
34
+ import { requireApiKey } from "../session.js";
35
+ function asDevices(value) {
36
+ if (!isRecord(value))
37
+ throw new NmtsError("The server's answer was not an object.");
38
+ const rows = value["sessions"];
39
+ if (!Array.isArray(rows)) {
40
+ throw new NmtsError("The server listed the signed-in devices in a shape this version cannot read.", { nextStep: `Update this tool — \`npm install -g ${BINARY_NAME}\` — or read it in a browser.` });
41
+ }
42
+ const out = [];
43
+ for (const row of rows) {
44
+ if (!isRecord(row))
45
+ continue;
46
+ const id = row["id"];
47
+ const created = row["created_at"];
48
+ const used = row["last_used_at"];
49
+ const expires = row["expires_at"];
50
+ // ⛔ A ROW THIS CANNOT READ IS SKIPPED, NOT INVENTED. A device shown with made-up times is
51
+ // worse than one row fewer: somebody looking for a session they did not start would be
52
+ // reading a fiction.
53
+ if (typeof id !== "string" || typeof created !== "string")
54
+ continue;
55
+ out.push({
56
+ id,
57
+ // ⚠ WHETHER IT HAS A NAME, NOT THE NAME. The value is ciphertext this command cannot open,
58
+ // and printing base64 would be printing noise while implying it means something.
59
+ named: typeof row["label_ct"] === "string" && row["label_ct"] !== "",
60
+ created_at: created,
61
+ last_used_at: typeof used === "string" ? used : created,
62
+ expires_at: typeof expires === "string" ? expires : created,
63
+ current: row["current"] === true,
64
+ });
65
+ }
66
+ return out;
67
+ }
68
+ export async function devices(options = {}) {
69
+ const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
70
+ // ⛔ THE KEY AND NOT THE NMTS KEY. This read needs no code, so asking for one would refuse a
71
+ // run over a credential it never uses — and on a machine holding only a key it would make
72
+ // the one command about that machine's own access the one command it cannot run.
73
+ const apiKey = requireApiKey();
74
+ const stored = readCredentialsFile();
75
+ const server = resolveServer(options.server ?? stored?.server);
76
+ if (options.signOut !== undefined)
77
+ return await signOut(server, apiKey, options, say);
78
+ const rows = asDevices(await request(server, "/v1/account/sessions", { token: apiKey }));
79
+ if (options.json === true) {
80
+ say(JSON.stringify({ devices: rows }));
81
+ return 0;
82
+ }
83
+ if (rows.length === 0) {
84
+ say(`Nothing is signed in to this account.`);
85
+ say(``);
86
+ say(` An API key is not a session, so the credential this command used is not listed here.`);
87
+ say(` Signing in at ${HOME_URL} is what puts a row in this list.`);
88
+ return 0;
89
+ }
90
+ for (const row of rows) {
91
+ say(`${row.named ? "named device (the name is sealed)" : "unnamed device"}${row.current ? " ← this one" : ""}`);
92
+ say(` last used ${row.last_used_at}`);
93
+ say(` signed in ${row.created_at}`);
94
+ say(` runs out ${row.expires_at}`);
95
+ }
96
+ say(``);
97
+ say(` ${rows.length} signed in. The name each device was given is encrypted with your NMTS`);
98
+ say(` key, which the server has never had, so it is not shown here.`);
99
+ say(``);
100
+ // ⛔ IT SAYS WHAT THIS CANNOT DO, rather than leaving a reader to find out by trying. A refusal
101
+ // discovered by running something is a refusal an agent retries.
102
+ say(` \`${BINARY_NAME} devices --sign-out <id>\` (or \`all\`) ends one — a person's act that needs the`);
103
+ say(` NMTS key on this machine; a key alone cannot, so a stolen key cannot end the browser's.`);
104
+ return 0;
105
+ }
106
+ /** `--sign-out <id|all>`: the refusals first, then one question, then the proof and the request. */
107
+ async function signOut(server, apiKey, options, say) {
108
+ const target = (options.signOut ?? "").trim();
109
+ if (target === "")
110
+ throw new NmtsError(`--sign-out needs a device id, or \`all\`.`, { exitCode: 2 });
111
+ // The tier gate has already refused or asked by mode; `--yes` is the answer it took. Without
112
+ // it, the question is put here, to a person at a terminal.
113
+ const ask = options.readLine ?? promptLine;
114
+ if (options.yes !== true && options.readLine === undefined && !stdinIsATerminal()) {
115
+ throw new NmtsError("There is no terminal to answer in (stdin is not a TTY).", {
116
+ exitCode: 3,
117
+ nextStep: `Run this where a person can answer the question it asks, or with --yes after they have.`,
118
+ });
119
+ }
120
+ const held = await requireAccountCode();
121
+ const what = target === "all" ? "every device signed in to this account" : `device ${target}`;
122
+ if (options.yes !== true) {
123
+ const answer = (await ask(`Sign ${what} out? Whoever is there is signed out at once. [y/N] `)).trim();
124
+ if (answer !== "y" && answer !== "Y") {
125
+ say(`Nothing was signed out.`);
126
+ return 1;
127
+ }
128
+ }
129
+ // ⛔ THE PROOF IS BUILT FOR THIS ONE REQUEST AND NOTHING KEEPS IT.
130
+ const accountProof = await accountProofFor({ code: held.code, source: held.source });
131
+ if (target === "all") {
132
+ const answered = await request(server, "/v1/account/sessions", { method: "DELETE", token: apiKey, accountProof });
133
+ const revoked = isRecord(answered) && typeof answered["revoked"] === "number" ? answered["revoked"] : null;
134
+ say(revoked === null ? `Signed out everywhere.` : `Signed out ${revoked} device${revoked === 1 ? "" : "s"}.`);
135
+ return 0;
136
+ }
137
+ await request(server, `/v1/account/sessions/${encodeURIComponent(target)}`, {
138
+ method: "DELETE",
139
+ token: apiKey,
140
+ accountProof,
141
+ });
142
+ say(`Signed device ${target} out.`);
143
+ return 0;
144
+ }
@@ -0,0 +1,35 @@
1
+ export interface DocumentOptions {
2
+ server?: string | undefined;
3
+ /** Machine-readable output, where the command has one. */
4
+ json?: boolean;
5
+ /** Keep the document as a file rather than printing it. */
6
+ save?: boolean;
7
+ /** Which directory `--save` writes into. Default: this one. */
8
+ out?: string | undefined;
9
+ /** `terms`/`privacy`: which language. Default: English, which is the canonical text. */
10
+ lang?: string | undefined;
11
+ /** `terms`: the board's terms rather than the service's. */
12
+ board?: boolean;
13
+ write?: (line: string) => void;
14
+ }
15
+ /**
16
+ * The three verbs, dispatched from one module.
17
+ *
18
+ * ⛔ ONE ENTRY POINT BECAUSE THEY ARE ONE SUBJECT — the documents this service publishes — and
19
+ * because every command in `main.ts` is loaded only when it is the command being run. Three
20
+ * entries there would be three imports to keep in step for one file.
21
+ */
22
+ export declare function runDocuments(command: string, options: DocumentOptions & {
23
+ id?: string | undefined;
24
+ }): Promise<number>;
25
+ /**
26
+ * `nmts notices` — the board, one notice, or one notice kept as a file.
27
+ *
28
+ * The id, when there is one, is the operand: `nmts notices <id>` prints it and
29
+ * `nmts notices --save <id>` writes it. With no id the command lists.
30
+ */
31
+ export declare function notices(options?: DocumentOptions & {
32
+ id?: string | undefined;
33
+ }): Promise<number>;
34
+ /** `nmts terms` and `nmts privacy` — the published document, printed or kept. */
35
+ export declare function legal(which: "terms" | "privacy", options?: DocumentOptions): Promise<number>;
@@ -0,0 +1,212 @@
1
+ // `nmts notices`, `nmts terms`, `nmts privacy` — the three documents a person is owed, read from
2
+ // the terminal instead of from a browser.
3
+ //
4
+ // ⛔ WHY A COMMAND-LINE TOOL NEEDS THEM. The notice board is where an interruption, an incident,
5
+ // and the seven-day warning before a new version of the Terms takes effect are posted, and the
6
+ // Terms and the Privacy Policy are the documents an account is held to. Until now all three
7
+ // lived only on screens. An account driven from a terminal — which is most of what this tool
8
+ // is for — could be governed by a document it had no way to read, and told about a change it
9
+ // had no way to see. That is the whole reason these exist.
10
+ //
11
+ // ⛔ THEY ASK FOR NOTHING AND CARRY NOTHING. No NMTS key, no API key, no session: these are
12
+ // public documents and the routes serving them are public. Nothing here opens a session, so
13
+ // reading the terms cannot fail because a credential is stale, and reading them says nothing
14
+ // to the server about who read them.
15
+ //
16
+ // ⛔ THE BYTES ARE THE BROWSER'S BYTES. `--save` writes what the server sends under the name the
17
+ // server's own `Content-Disposition` gives it — the same name and the same bytes the browser's
18
+ // download button produces. A copy kept from here and a copy kept from a screen are the same
19
+ // file, which is what makes either one worth keeping.
20
+ import { existsSync, statSync, writeFileSync } from "node:fs";
21
+ import { isAbsolute, resolve } from "node:path";
22
+ import { HttpError, request } from "../api.js";
23
+ import { NmtsError } from "../errors.js";
24
+ import { isRecord } from "../guards.js";
25
+ import { BINARY_NAME } from "../product.js";
26
+ import { destinationFor } from "../safe-path.js";
27
+ import { resolveServer } from "../server.js";
28
+ /** The three ids `GET /api/legal/{id}` serves, and the two the command line names. */
29
+ const LEGAL_IDS = { terms: "terms", privacy: "privacy", board: "board-terms" };
30
+ /**
31
+ * The three verbs, dispatched from one module.
32
+ *
33
+ * ⛔ ONE ENTRY POINT BECAUSE THEY ARE ONE SUBJECT — the documents this service publishes — and
34
+ * because every command in `main.ts` is loaded only when it is the command being run. Three
35
+ * entries there would be three imports to keep in step for one file.
36
+ */
37
+ export async function runDocuments(command, options) {
38
+ if (command === "notices")
39
+ return await notices(options);
40
+ return await legal(command === "terms" ? "terms" : "privacy", options);
41
+ }
42
+ // ── The notice board ────────────────────────────────────────────────────────────────────────
43
+ /**
44
+ * `nmts notices` — the board, one notice, or one notice kept as a file.
45
+ *
46
+ * The id, when there is one, is the operand: `nmts notices <id>` prints it and
47
+ * `nmts notices --save <id>` writes it. With no id the command lists.
48
+ */
49
+ export async function notices(options = {}) {
50
+ const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
51
+ const base = resolveServer(options.server);
52
+ const id = options.id;
53
+ if (id === undefined) {
54
+ if (options.save === true) {
55
+ throw new NmtsError("Say which notice to save.", {
56
+ exitCode: 2,
57
+ nextStep: `\`${BINARY_NAME} notices --save <id>\`. Run \`${BINARY_NAME} notices\` to see the ids.`,
58
+ });
59
+ }
60
+ return await listNotices(base, options, say);
61
+ }
62
+ const answer = await noticeBody(base, id);
63
+ if (options.save !== true) {
64
+ say(answer.text.replace(/\n$/u, ""));
65
+ return 0;
66
+ }
67
+ say(`Wrote ${keep(answer, options.out)}`);
68
+ return 0;
69
+ }
70
+ async function listNotices(base, options, say) {
71
+ const answer = await request(base, "/api/notices");
72
+ if (options.json === true) {
73
+ say(JSON.stringify(answer));
74
+ return 0;
75
+ }
76
+ const rows = noticeRows(answer);
77
+ if (rows.length === 0) {
78
+ say("No notices have been posted.");
79
+ return 0;
80
+ }
81
+ for (const row of rows)
82
+ say(`${row.date} ${row.id} ${row.title}`);
83
+ say("");
84
+ say(`Read one with \`${BINARY_NAME} notices <id>\`, or keep it as a dated file with ` +
85
+ `\`${BINARY_NAME} notices --save <id>\`.`);
86
+ return 0;
87
+ }
88
+ /**
89
+ * The rows of the feed, read as the route declares them.
90
+ *
91
+ * ⛔ THE TITLE IS TAKEN FROM THE ENGLISH SIDE and the row is skipped when there is no id or date,
92
+ * rather than printed with a blank where one should be. A line missing the id is a line nobody
93
+ * can act on, and printing it invites a person to ask this tool for a notice by a name it will
94
+ * refuse.
95
+ */
96
+ function noticeRows(answer) {
97
+ const list = isRecord(answer) ? answer["notices"] : undefined;
98
+ if (!Array.isArray(list)) {
99
+ throw new NmtsError("The server's answer did not carry a list of notices.", {
100
+ exitCode: 1,
101
+ nextStep: "Report it rather than retrying — the shape, not the network, is wrong.",
102
+ });
103
+ }
104
+ const rows = [];
105
+ for (const row of list) {
106
+ if (!isRecord(row))
107
+ continue;
108
+ const id = row["id"];
109
+ const date = row["date"];
110
+ const title = isRecord(row["title"]) ? row["title"]["en"] : undefined;
111
+ if (typeof id !== "string" || typeof date !== "string")
112
+ continue;
113
+ rows.push({ id, date, title: typeof title === "string" ? title : "" });
114
+ }
115
+ return rows;
116
+ }
117
+ /** One notice's text, with a refusal that names the way to find the ids that exist. */
118
+ async function noticeBody(base, id) {
119
+ try {
120
+ return await request(base, `/api/notices/${encodeURIComponent(id)}`, { as: "text" });
121
+ }
122
+ catch (error) {
123
+ // ⛔ ONLY A 404 BECOMES THIS REFUSAL. A network failure or a proxy's error page is a different
124
+ // problem with a different remedy, and telling somebody to check the id when the server was
125
+ // unreachable sends them looking in the wrong place.
126
+ if (error instanceof HttpError && error.status === 404) {
127
+ throw new NmtsError(`No notice has been posted with the id ${id}.`, {
128
+ exitCode: 4,
129
+ nextStep: `Run \`${BINARY_NAME} notices\` to see the notices there are, with their ids.`,
130
+ });
131
+ }
132
+ throw error;
133
+ }
134
+ }
135
+ // ── The Terms and the Privacy Policy ────────────────────────────────────────────────────────
136
+ /** `nmts terms` and `nmts privacy` — the published document, printed or kept. */
137
+ export async function legal(which, options = {}) {
138
+ const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
139
+ const base = resolveServer(options.server);
140
+ const lang = languageOf(options.lang);
141
+ if (options.board === true && which !== "terms") {
142
+ throw new NmtsError("--board belongs to the terms, not the privacy policy.", {
143
+ exitCode: 2,
144
+ nextStep: `\`${BINARY_NAME} terms --board\` is the message board's terms. \`${BINARY_NAME} privacy\` has one document.`,
145
+ });
146
+ }
147
+ const id = options.board === true ? LEGAL_IDS.board : LEGAL_IDS[which];
148
+ const answer = await request(base, `/api/legal/${id}?lang=${lang}`, { as: "text" });
149
+ if (options.save !== true) {
150
+ say(answer.text.replace(/\n$/u, ""));
151
+ return 0;
152
+ }
153
+ say(`Wrote ${keep(answer, options.out)}`);
154
+ return 0;
155
+ }
156
+ /**
157
+ * Which language to ask for.
158
+ *
159
+ * ⛔ IT REFUSES RATHER THAN FALLING BACK. The route treats anything that is not `ko` as English,
160
+ * so a typo would silently hand somebody the wrong document while looking like it worked — and
161
+ * the two versions of a legal document are not interchangeable to whoever is reading one.
162
+ */
163
+ function languageOf(lang) {
164
+ if (lang === undefined || lang === "en")
165
+ return "en";
166
+ if (lang === "ko")
167
+ return "ko";
168
+ throw new NmtsError(`This tool does not have a version in "${lang}".`, {
169
+ exitCode: 2,
170
+ nextStep: "--lang takes en or ko. English is the canonical text.",
171
+ });
172
+ }
173
+ // ── Keeping a copy ──────────────────────────────────────────────────────────────────────────
174
+ /**
175
+ * Write a document out under the name the server gave it, never over something already there.
176
+ *
177
+ * ⛔ IT DOES NOT REPLACE, AND THERE IS NO --force. These files carry a version and a date in their
178
+ * names, so two documents never want the same one: a name already taken means the same document
179
+ * is already kept, and quietly rewriting it would destroy a copy somebody kept on purpose — the
180
+ * copy that says what they were told and when.
181
+ */
182
+ function keep(answer, out) {
183
+ if (answer.filename === null) {
184
+ throw new NmtsError("The server did not say what to call this document.", {
185
+ exitCode: 1,
186
+ nextStep: "Nothing was written. Print it instead and redirect it to a name of your choosing.",
187
+ });
188
+ }
189
+ const directory = out === undefined ? process.cwd() : isAbsolute(out) ? out : resolve(process.cwd(), out);
190
+ if (existsSync(directory) && !statSync(directory).isDirectory()) {
191
+ throw new NmtsError(`${directory} is a file, and --out names the directory to write into.`, {
192
+ exitCode: 2,
193
+ nextStep: `Nothing was written. Pass --out with a directory, or leave it out to write into ${process.cwd()}.`,
194
+ });
195
+ }
196
+ // The name came over the network; this is what contains it to the chosen directory.
197
+ const destination = destinationFor(directory, answer.filename);
198
+ try {
199
+ writeFileSync(destination, answer.text, { flag: "wx" });
200
+ }
201
+ catch (error) {
202
+ if (error instanceof Error && Reflect.get(error, "code") === "EEXIST") {
203
+ throw new NmtsError(`${destination} is already there.`, {
204
+ exitCode: 4,
205
+ nextStep: `Nothing was written, and the copy already there was not touched. Pass --out to write ` +
206
+ `into another directory, or move that file aside.`,
207
+ });
208
+ }
209
+ throw error;
210
+ }
211
+ return destination;
212
+ }
@@ -0,0 +1,5 @@
1
+ export interface EnvOptions {
2
+ json?: boolean;
3
+ write?: (line: string) => void;
4
+ }
5
+ export declare function env(options?: EnvOptions): number;
@@ -1,9 +1,10 @@
1
- // `nmts env` — what this machine is, and what that means for the account code.
1
+ // `nmts env` — what this machine is, and what that means for the NMTS key.
2
2
  //
3
3
  // ⛔ IT ASKS THE SERVER NOTHING AND NEEDS NO CREDENTIAL. It is the first command an agent can run
4
4
  // on a machine it has never seen, and a command that needed to be signed in to say "there is
5
5
  // nowhere safe to keep the code here" would say it too late.
6
6
  import { adviseFor, readEnvironment } from "../environment.js";
7
+ import { describeSighting } from "../agent-host.js";
7
8
  import { PASSPHRASE_ENV_VAR, resolveAccountCode, resolveApiKey, } from "../credentials.js";
8
9
  import { BINARY_NAME, SUPPORT_EMAIL, VERSION } from "../product.js";
9
10
  import { checkingIsOff, NO_CHECK_ENV_VAR, readCheck } from "../update-check.js";
@@ -107,8 +108,15 @@ export function env(options = {}) {
107
108
  say(` config ${environment.configDir}`);
108
109
  say(` terminal ${environment.interactive ? "yes" : "no — nothing here can be typed"}`);
109
110
  say(` browser ${environment.browserReachable ? "reachable" : "not reachable"}`);
111
+ // ⛔ "none found" IS NOT "no agent". Three of the five hosts this tool knows clear the
112
+ // environment before starting an MCP server, so an empty line here is exactly what running
113
+ // inside one of them looks like. The wording has to leave room for that or it reports a
114
+ // measurement as a conclusion.
115
+ say(` agent host ${environment.agentHosts.length === 0
116
+ ? "no marker in this environment — some agents clear it, so this is not proof there is none"
117
+ : environment.agentHosts.map(describeSighting).join("; ")}`);
110
118
  const found = (got, problem) => (got !== null ? `found — ${sourceWords(got.source)}` : problem !== null ? `⛔ REFUSED — ${problem}` : "not found");
111
- say(` account code ${found(code, codeProblem)}`);
119
+ say(` NMTS key ${found(code, codeProblem)}`);
112
120
  if (code?.source === "file-locked") {
113
121
  // ⛔ THE ANSWER AN AGENT NEEDS BEFORE IT RUNS ANYTHING ELSE. A sealed code with no way to
114
122
  // supply the passphrase is not a usable credential, and finding that out on the first
@@ -0,0 +1,13 @@
1
+ export interface EraseOptions {
2
+ server?: string | undefined;
3
+ network?: string | undefined;
4
+ json?: boolean;
5
+ /** Also destroy the treasury's storage under credit-paid files, before erasing them. */
6
+ releaseStorage?: boolean;
7
+ /** Under skip-permissions only: the tier gate's answer, given with a `--reason`. */
8
+ yes?: boolean;
9
+ write?: ((line: string) => void) | undefined;
10
+ /** Injected in tests: answers the one typed line. */
11
+ readLine?: ((question: string) => Promise<string>) | undefined;
12
+ }
13
+ export declare function erase(paths: readonly string[], options?: EraseOptions): Promise<number>;