@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,29 @@
1
+ import { type CredentialSource } from "./credentials.ts";
2
+ export interface OpenedCode {
3
+ code: string;
4
+ source: CredentialSource;
5
+ }
6
+ /**
7
+ * The NMTS key for this run, opening a sealed one if that is what is stored.
8
+ *
9
+ * Returns null when there is nothing to use — the caller decides whether that is an error, which
10
+ * differs between `whoami` (say so quietly) and `put` (refuse).
11
+ */
12
+ export interface OpenOptions {
13
+ /**
14
+ * May this run stop and ask on the terminal?
15
+ *
16
+ * ⛔ FALSE FOR `nmts mcp`, AND THAT IS NOT TIDINESS. Its stdin carries the protocol: a prompt
17
+ * there consumes the client's first message as a passphrase guess, and the answer it prints
18
+ * lands on the wire as bytes the other end cannot parse. An MCP server that cannot open its
19
+ * credential must say so and exit, not negotiate.
20
+ */
21
+ allowPrompt?: boolean;
22
+ }
23
+ export declare function openAccountCode(options?: OpenOptions): Promise<OpenedCode | null>;
24
+ /**
25
+ * The code, or a refusal naming what to do about it. For commands that cannot proceed without it.
26
+ *
27
+ * ⛔ The message never mentions the code itself, only where one could come from.
28
+ */
29
+ export declare function requireAccountCode(options?: OpenOptions): Promise<OpenedCode>;
@@ -1,4 +1,4 @@
1
- // Getting the account code this run should actually use — including opening a sealed one.
1
+ // Getting the NMTS key this run should actually use — including opening a sealed one.
2
2
  //
3
3
  // ⛔ WHY IT IS NOT IN `credentials.ts`. That module answers "where is the code"; this one answers
4
4
  // "may this run have it, and can it be opened". Putting the second question in the first module
@@ -47,7 +47,7 @@ async function readPassphrase(allowPrompt) {
47
47
  if (fromEnv !== undefined && fromEnv.length > 0)
48
48
  return fromEnv;
49
49
  if (!allowPrompt || !stdinIsATerminal()) {
50
- throw new NmtsError(`The stored account code is sealed with a passphrase.`, {
50
+ throw new NmtsError(`The stored NMTS key is sealed with a passphrase.`, {
51
51
  exitCode: 3,
52
52
  nextStep: [
53
53
  allowPrompt
@@ -61,7 +61,7 @@ async function readPassphrase(allowPrompt) {
61
61
  ].join("\n"),
62
62
  });
63
63
  }
64
- return promptSecret(`Passphrase for the stored account code: `, PASSPHRASE_ENV_VAR);
64
+ return promptSecret(`Passphrase for the stored NMTS key: `, PASSPHRASE_ENV_VAR);
65
65
  }
66
66
  /**
67
67
  * The code, or a refusal naming what to do about it. For commands that cannot proceed without it.
@@ -0,0 +1,38 @@
1
+ import { NmtsError } from "./errors.ts";
2
+ /** What gets written. Every field is needed to open it again; none of them is a secret. */
3
+ export interface LockedCode {
4
+ /** Format version of THIS file, not of the NMTS crypto format. */
5
+ v: 1;
6
+ kdf: "scrypt";
7
+ n: number;
8
+ r: number;
9
+ p: number;
10
+ /** base64 */
11
+ salt: string;
12
+ /** base64 */
13
+ nonce: string;
14
+ /** base64, ciphertext followed by the 16-byte tag. */
15
+ ct: string;
16
+ }
17
+ /** Thrown when the passphrase does not open the file. ⛔ Never says how close it was. */
18
+ export declare class WrongPassphraseError extends NmtsError {
19
+ constructor();
20
+ }
21
+ /** Seal the code under a passphrase. The result is safe to write to a file. */
22
+ export declare function lockCode(code: string, passphrase: string): LockedCode;
23
+ /**
24
+ * Open a sealed code.
25
+ *
26
+ * ⛔ The tag is checked before a single byte is returned — that is what `final()` does for GCM, and
27
+ * it is why a wrong passphrase cannot yield a plausible-looking wrong code.
28
+ */
29
+ export declare function unlockCode(locked: LockedCode, passphrase: string): string;
30
+ /** Shape check for something read off disk. ⛔ A parser, not an assertion: the file is input. */
31
+ export declare function isLockedCode(value: unknown): value is LockedCode;
32
+ /**
33
+ * Are these two passphrases the same? Used only to catch a typo when one is being set.
34
+ *
35
+ * ⚠ Constant-time because it costs nothing to be. Neither value is secret to this process, but a
36
+ * comparison that short-circuits is a habit worth not having near a passphrase.
37
+ */
38
+ export declare function samePassphrase(a: string, b: string): boolean;
@@ -1,6 +1,6 @@
1
- // Locking the account code with a passphrase, so what is on disk is not the code.
1
+ // Locking the NMTS key with a passphrase, so what is on disk is not the code.
2
2
  //
3
- // WHY THIS EXISTS. `nmts login` used to write the account code in the clear at mode 600, which is
3
+ // WHY THIS EXISTS. `nmts login` used to write the NMTS key in the clear at mode 600, which is
4
4
  // what `gh`, `aws` and `docker login` do. Mode 600 answers exactly one question — "can another
5
5
  // user on this machine read it" — and answers nothing about a backup, a copied home directory,
6
6
  // a synced folder, a container image layer, or a disk pulled out of a laptop. A passphrase
@@ -15,7 +15,7 @@
15
15
  // the code is "safe".
16
16
  //
17
17
  // ⛔ NODE BUILT-INS ONLY. `scrypt` and AES-256-GCM are in `node:crypto`; a password-hashing
18
- // dependency would put somebody else's code on the path the account code travels, for a
18
+ // dependency would put somebody else's code on the path the NMTS key travels, for a
19
19
  // function the platform already ships. This is local storage, not the NMTS crypto format —
20
20
  // NCF-3 governs what leaves this machine, and nothing here does.
21
21
  import { createCipheriv, createDecipheriv, randomBytes, scryptSync, timingSafeEqual, } from "node:crypto";
@@ -78,10 +78,10 @@ const TAG_BYTES = 16;
78
78
  /** Thrown when the passphrase does not open the file. ⛔ Never says how close it was. */
79
79
  export class WrongPassphraseError extends NmtsError {
80
80
  constructor() {
81
- super("That passphrase does not open the stored account code.", {
81
+ super("That passphrase does not open the stored NMTS key.", {
82
82
  exitCode: 3,
83
83
  nextStep: "Try again. If the passphrase is lost, the stored copy cannot be recovered — sign in " +
84
- "again with the account code itself.",
84
+ "again with the NMTS key itself.",
85
85
  });
86
86
  this.name = "WrongPassphraseError";
87
87
  }
@@ -105,10 +105,10 @@ function refuseCost(locked) {
105
105
  const { n, r, p } = locked;
106
106
  const whole = (v) => Number.isInteger(v) && v >= 1;
107
107
  const refuse = () => {
108
- throw new NmtsError("The stored account code names a key-derivation cost this version refuses.", {
108
+ throw new NmtsError("The stored NMTS key names a key-derivation cost this version refuses.", {
109
109
  exitCode: 1,
110
110
  nextStep: "The file has been edited, or was written by a newer version of this tool. Sign in again " +
111
- "with the account code itself.",
111
+ "with the NMTS key itself.",
112
112
  });
113
113
  };
114
114
  if (!whole(n) || !whole(r) || !whole(p))
@@ -136,7 +136,7 @@ function deriveKey(passphrase, salt, n, r, p) {
136
136
  return scryptSync(Buffer.from(passphrase, "utf8"), salt, KEY_BYTES, { N: n, r, p, maxmem: MAXMEM });
137
137
  }
138
138
  catch (error) {
139
- throw new NmtsError("The stored account code asks for a key derivation this machine refused.", {
139
+ throw new NmtsError("The stored NMTS key asks for a key derivation this machine refused.", {
140
140
  exitCode: 1,
141
141
  nextStep: `Cause: ${error instanceof Error ? error.message : String(error)}`,
142
142
  });
@@ -0,0 +1,67 @@
1
+ import { type Autonomy } from "./autonomy.ts";
2
+ /** What to do with a name that is already in use. Mirrors the browser's two buttons. */
3
+ export type OnCollision = "rename" | "overwrite";
4
+ export declare const COLLISION_CHOICES: readonly OnCollision[];
5
+ /** What each choice does, in the words the tool prints. One line each. */
6
+ export declare const COLLISION_MEANS: Readonly<Record<OnCollision, string>>;
7
+ /**
8
+ * What a run asked for on the command line, or undefined for "use this machine's setting".
9
+ *
10
+ * ⛔ AN UNKNOWN WORD IS REFUSED, NOT ROUNDED DOWN. Silently reading `--on-collision overwite` as
11
+ * the safe answer would look like it worked, and the person would find out from the drive.
12
+ */
13
+ export declare function parseAsked(typed: string | undefined): OnCollision | undefined;
14
+ /** What is written down when nobody has chosen. */
15
+ export declare const DEFAULT_COLLISION: OnCollision;
16
+ /**
17
+ * What this machine is set to.
18
+ *
19
+ * ⛔ Unreadable counts as `rename`, for the same reason autonomy unreadable counts as off: the
20
+ * fail-safe direction for "I do not know" is the one that destroys nothing.
21
+ */
22
+ export declare function currentChoice(): OnCollision;
23
+ /** Has anybody answered on this machine? Used to know whether setup still has to ask. */
24
+ export declare function hasChosen(): boolean;
25
+ /** Write the choice down, with the date and the version that asked. */
26
+ export declare function setChoice(choice: OnCollision, version: string, now: Date): void;
27
+ /** Forget the answer, so setup asks again. */
28
+ export declare function forgetChoice(): void;
29
+ /** What decided, so the tool can say so rather than acting silently. */
30
+ export interface Decision {
31
+ readonly choice: OnCollision;
32
+ /**
33
+ * What settled it.
34
+ *
35
+ * `setting` — what a person answered at setup, or the default when nobody has.
36
+ * `asked-for` — an agent asked for this run to overwrite, and a mode allows it.
37
+ * `agent-refused` — an agent asked to overwrite while no mode is on, so it renames instead.
38
+ */
39
+ readonly by: "setting" | "asked-for" | "agent-refused";
40
+ }
41
+ /**
42
+ * What to do with this collision, and what settled it.
43
+ *
44
+ * ⛔ A STORED ANSWER IS A PERSON'S ANSWER AND IS NOT SECOND-GUESSED. Setup asks while somebody is
45
+ * definitely there; overriding that later because autonomy happens to be off would mean the
46
+ * tool ignoring the one answer it actually has from a person.
47
+ *
48
+ * ⛔ WHAT THE MODES GATE IS THE OTHER THING: an agent deciding, for THIS run, to overwrite when
49
+ * nobody said so. That is the case the owner ruled on -- without a mode the agent picks rename
50
+ * -- and it is enforced here rather than written in the instructions and hoped for.
51
+ *
52
+ * ⛔ THE OVERRIDE IS ONE-WAY. A mode can let `overwrite` through; nothing here turns a `rename`
53
+ * into an `overwrite`.
54
+ */
55
+ export declare function decide(
56
+ /** What this run asked for, if anything. `undefined` means "use what this machine is set to". */
57
+ askedFor?: OnCollision, setting?: OnCollision, mode?: Autonomy): Decision;
58
+ /** How the two answers are numbered where setup asks. Kept here so the question and the reading agree. */
59
+ export declare const ANSWER_NUMBER: Readonly<Record<OnCollision, string>>;
60
+ /**
61
+ * What somebody typed at the setup question.
62
+ *
63
+ * ⛔ ONLY THE EXACT NUMBER FOR OVERWRITE COUNTS, and everything else is the safe answer. A typo, an
64
+ * empty line, a closed pipe, a stray space, `y`, `yes` — none of them mean "delete my files".
65
+ * The answer that destroys something has to be typed on purpose.
66
+ */
67
+ export declare function readAnswer(typed: string): OnCollision;
package/dist/collision.js CHANGED
@@ -115,7 +115,7 @@ askedFor, setting = currentChoice(), mode = currentMode()) {
115
115
  return { choice: setting, by: "setting" };
116
116
  if (askedFor === "rename")
117
117
  return { choice: "rename", by: "asked-for" };
118
- if (mode === "off")
118
+ if (mode === "default")
119
119
  return { choice: "rename", by: "agent-refused" };
120
120
  return { choice: "overwrite", by: "asked-for" };
121
121
  }
@@ -0,0 +1,11 @@
1
+ export interface AcceptTermsOptions {
2
+ server?: string | undefined;
3
+ write?: ((line: string) => void) | undefined;
4
+ /** Injected in tests: answers the two typed lines, in order. */
5
+ readLine?: ((question: string) => Promise<string>) | undefined;
6
+ /** `--accept-terms <version>`: the version the person said they accept, relayed by an agent. */
7
+ terms?: string | undefined;
8
+ /** `--accept-privacy <version>`: the version the person said they have read. */
9
+ privacy?: string | undefined;
10
+ }
11
+ export declare function acceptTerms(options?: AcceptTermsOptions): Promise<number>;
@@ -0,0 +1,106 @@
1
+ // `nmts accept-terms` — a PERSON accepts the documents in force, from the terminal.
2
+ //
3
+ // ⛔ WHY IT EXISTS (2026-09-06). When a new version of the Terms takes effect the server
4
+ // refuses this account's uploads and shares until the new version is accepted (Terms 15.5). An
5
+ // account driven from a terminal holds an API key and the NMTS key and never a session,
6
+ // and a key is not allowed to accept — a key accepting would be a program consenting on
7
+ // somebody's behalf. Until this command the only way out was a browser. Now the same record is
8
+ // written from here, proved the way a browser sign-in is proved: by the NMTS key.
9
+ //
10
+ // ⛔ IT IS A PERSON'S ACT, AND THREE THINGS KEEP IT ONE. It is refused in mode auto and under
11
+ // --skip-permissions before the code is even opened. It needs a terminal to type into — there is
12
+ // no `--terms <v>` flag, because a flag is what an agent would pass. And the two versions are
13
+ // TYPED, not confirmed with a key press: typing the version string is what the browser's screen
14
+ // asks for too, and it is the smallest act that cannot be done without having looked.
15
+ //
16
+ // ⛔ TWO ANSWERS, NOT ONE, for the same reason the browser has two controls. Consent to the Terms is
17
+ // a contract; the Privacy Policy is not consented to — its own §7.3 rests the processing on
18
+ // contract and legitimate interests — so the second line acknowledges READING it. The call to the
19
+ // server is the same pair the screen sends.
20
+ //
21
+ // ⛔ WHAT LEAVES THIS MACHINE is the account id, the sign-in proof (`account-proof.ts` says what
22
+ // that is and why it decrypts nothing) and the two typed versions. The NMTS key does not.
23
+ import { accountProofFor } from "../account-proof.js";
24
+ import { identityOf } from "../account.js";
25
+ import { request } from "../api.js";
26
+ import { requireAccountCode } from "../code-access.js";
27
+ import { readCredentialsFile, resolveApiKey } from "../credentials.js";
28
+ import { NmtsError } from "../errors.js";
29
+ import { isRecord } from "../guards.js";
30
+ import { BINARY_NAME, HOME_URL } from "../product.js";
31
+ import { promptLine, stdinIsATerminal } from "../prompt.js";
32
+ import { resolveServer } from "../server.js";
33
+ export async function acceptTerms(options = {}) {
34
+ const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
35
+ // The tier gate has already refused or asked by mode. The two versions are typed here by a
36
+ // person at a terminal, or — when an agent relays the person's acceptance — named on the
37
+ // command line: `--accept-terms <version> --accept-privacy <version>`.
38
+ const relayed = options.terms !== undefined && options.privacy !== undefined;
39
+ const ask = options.readLine ?? promptLine;
40
+ if (!relayed && options.readLine === undefined && !stdinIsATerminal()) {
41
+ throw new NmtsError("There is no terminal to type into (stdin is not a TTY).", {
42
+ exitCode: 3,
43
+ nextStep: `Run this where a person can type the two versions, or — after they have said so — ` +
44
+ `\`${BINARY_NAME} accept-terms --accept-terms <version> --accept-privacy <version> --yes\`.`,
45
+ });
46
+ }
47
+ const held = await requireAccountCode();
48
+ const stored = readCredentialsFile();
49
+ const server = resolveServer(options.server ?? stored?.server);
50
+ // The key is only for READING where the account stands; the acceptance itself needs none — an
51
+ // account whose key cannot even be minted until it accepts (`key new` is refused too) still has
52
+ // this way in.
53
+ const key = resolveApiKey();
54
+ const standing = key === null ? null : await standingOf(server, key.key);
55
+ if (standing !== null && !standing.owed) {
56
+ say(`This account has already accepted the documents in force ` +
57
+ `(Terms of Service ${standing.terms} · Privacy Policy ${standing.privacy}). Nothing to do.`);
58
+ return 0;
59
+ }
60
+ if (standing === null) {
61
+ say(`No API key on this machine, so where this account stands could not be read.`);
62
+ say(`Each document prints its version at the top.`);
63
+ }
64
+ else {
65
+ say(`In force: Terms of Service ${standing.terms} · Privacy Policy ${standing.privacy}.`);
66
+ say(`This account has not accepted this pair, so uploads and shares are refused until it does.`);
67
+ }
68
+ say(`Read them first: \`${BINARY_NAME} terms\` · \`${BINARY_NAME} privacy\` · what changed: ` +
69
+ `\`${BINARY_NAME} notices\` (or ${HOME_URL}/notices).`);
70
+ say(``);
71
+ const terms = (options.terms ?? (await ask(`Type the Terms of Service version you have read and accept: `))).trim();
72
+ const privacy = (options.privacy ?? (await ask(`Type the Privacy Policy version you have read: `))).trim();
73
+ if (terms === "" || privacy === "") {
74
+ say(`Nothing was accepted.`);
75
+ return 1;
76
+ }
77
+ const identity = await identityOf(held.code);
78
+ // ⛔ THE PROOF IS BUILT FOR THIS ONE REQUEST AND NOTHING KEEPS IT. `accountProofFor` also asks
79
+ // for the agreement that covers sending it when the code came from an environment variable.
80
+ const authSecret = await accountProofFor({ code: held.code, source: held.source });
81
+ await request(server, "/v1/account/accept-terms/by-code", {
82
+ method: "POST",
83
+ body: {
84
+ account_id: identity.accountId,
85
+ auth_secret: authSecret,
86
+ terms_version: terms,
87
+ privacy_version: privacy,
88
+ },
89
+ });
90
+ say(`Recorded: this account accepts the Terms of Service ${terms} and has read the Privacy Policy ${privacy}.`);
91
+ say(`Uploads and shares are open again.`);
92
+ return 0;
93
+ }
94
+ async function standingOf(server, apiKey) {
95
+ const answer = await request(server, "/v1/account/summary", { token: apiKey });
96
+ const terms = isRecord(answer) ? answer["terms"] : undefined;
97
+ if (!isRecord(terms))
98
+ throw new NmtsError("The server described the account in a shape this version cannot read.");
99
+ const required = terms["required_terms_version"];
100
+ const privacy = terms["required_privacy_version"];
101
+ // No pair in force means the gate is off and nothing is owed — the same reading `balance` makes.
102
+ if (typeof required !== "string" || typeof privacy !== "string") {
103
+ return { owed: false, terms: "none", privacy: "none" };
104
+ }
105
+ return { owed: terms["acceptance_required"] === true, terms: required, privacy };
106
+ }
@@ -0,0 +1,7 @@
1
+ export interface BalanceOptions {
2
+ server?: string | undefined;
3
+ network?: string | undefined;
4
+ json?: boolean;
5
+ write?: (line: string) => void;
6
+ }
7
+ export declare function balance(options?: BalanceOptions): Promise<number>;
@@ -48,6 +48,14 @@ function asSummary(value) {
48
48
  soonest_expiry: typeof expiry === "string" ? expiry : null,
49
49
  file_cap: num(credits["file_cap"], "credits.file_cap"),
50
50
  daily_cap: num(credits["daily_cap"], "credits.daily_cap"),
51
+ // Deposits arrived with the server that returns them; an older server simply has none.
52
+ held: typeof credits["held"] === "number" ? credits["held"] : 0,
53
+ deposits: typeof credits["deposits_held"] === "number" ? credits["deposits_held"] : 0,
54
+ // ⛔ THE SERVER'S TWO NUMBERS, or none. A build that filled these in from a constant of its
55
+ // own would print what it believes rather than what the ledger will do, which is exactly
56
+ // the mistake the two ceilings above are read this way to avoid.
57
+ deposit_max: typeof credits["deposit_max"] === "number" ? credits["deposit_max"] : 0,
58
+ deposit_default: typeof credits["deposit_default"] === "number" ? credits["deposit_default"] : 0,
51
59
  },
52
60
  quota: { granted: num(quota["granted"], "quota.granted"), used: num(quota["used"], "quota.used") },
53
61
  storage: {
@@ -55,8 +63,33 @@ function asSummary(value) {
55
63
  earliest_expiry_epoch: typeof epoch === "number" ? epoch : null,
56
64
  },
57
65
  terms: { acceptance_required: isRecord(terms) && terms["acceptance_required"] === true },
66
+ ai_account: value["ai_account"] === true,
67
+ deposits: depositRows(credits["deposits"]),
58
68
  };
59
69
  }
70
+ /**
71
+ * The per-file deposit rows, when the answer carries them.
72
+ *
73
+ * ⛔ ABSENCE IS NOT ZERO ROWS DRESSED UP. The narrow read this command makes answers with the two
74
+ * totals and no list, so nothing is printed rather than a list claiming this account has no
75
+ * deposits. A row this version cannot read is dropped for the same reason: a file whose
76
+ * set-aside is a string is a file this build cannot say anything true about.
77
+ */
78
+ function depositRows(value) {
79
+ if (!Array.isArray(value))
80
+ return [];
81
+ const rows = [];
82
+ for (const row of value) {
83
+ if (!isRecord(row))
84
+ continue;
85
+ const set = row["deposit_credits"];
86
+ const spent = row["spent_credits"];
87
+ if (typeof set !== "number" || typeof spent !== "number")
88
+ continue;
89
+ rows.push({ deposit_credits: set, spent_credits: spent });
90
+ }
91
+ return rows;
92
+ }
60
93
  function plural(n, one, many) {
61
94
  return `${n} ${n === 1 ? one : many}`;
62
95
  }
@@ -69,6 +102,13 @@ export async function balance(options = {}) {
69
102
  return 0;
70
103
  }
71
104
  const { credits, quota, storage } = summary;
105
+ // ⛔ FIRST, AND NOT UNDER THE NUMBERS. Whoever is reading has to know WHICH account these
106
+ // figures belong to before they mean anything: an AI account has its own key, its own wallet
107
+ // and its own empty drive, so "nothing here" is the ordinary answer rather than a loss.
108
+ if (summary.ai_account) {
109
+ say(`AI account (not the main account)`);
110
+ say(``);
111
+ }
72
112
  say(`credits ${plural(credits.remaining, "credit", "credits")}`);
73
113
  // ⛔ SAID AS BYTES TOO, because "one credit" means nothing until you know what it buys. It is the
74
114
  // same number, not a second one — the server derives it from the same ledger read.
@@ -79,6 +119,19 @@ export async function balance(options = {}) {
79
119
  say(` soonest to lapse unused: ${credits.soonest_expiry}`);
80
120
  }
81
121
  say(`ceilings ${credits.file_cap} per file · ${credits.daily_cap} per day`);
122
+ if (credits.held > 0) {
123
+ // Tied up, not spent: each credit-paid file puts a deposit down, returned when its period ends.
124
+ say(`deposits ${plural(credits.held, "credit", "credits")} held on ${plural(credits.deposits, "stored file", "stored files")} — back when the storage period ends`);
125
+ // ⚠ Per file, and what has already gone out of it. A deposit is spent a fee at a time, so
126
+ // "set aside" and "spent" are two different numbers about the same file.
127
+ for (const row of summary.deposits) {
128
+ say(` ${row.deposit_credits} set aside · ${row.spent_credits} spent`);
129
+ }
130
+ }
131
+ if (credits.deposit_max > 0) {
132
+ say(` an upload sets ${credits.deposit_default} aside by default — ` +
133
+ `0 to ${credits.deposit_max}, \`${BINARY_NAME} deposit\` or --deposit for one upload`);
134
+ }
82
135
  say(`holding ${humanSize(quota.used)} across ${plural(storage.parts, "stored piece", "stored pieces")}`);
83
136
  if (storage.earliest_expiry_epoch !== null) {
84
137
  say(` the earliest lease ends at storage period ${storage.earliest_expiry_epoch} — ` +
@@ -87,7 +140,7 @@ export async function balance(options = {}) {
87
140
  if (summary.terms.acceptance_required) {
88
141
  say(``);
89
142
  say(`⛔ New terms are in force and this account has not accepted them.`);
90
- say(` A person has to read and accept them in a browser; nothing here can do it.`);
143
+ say(` A person reads and accepts them \`${BINARY_NAME} accept-terms\` here, or in a browser.`);
91
144
  }
92
145
  return 0;
93
146
  }
@@ -0,0 +1,24 @@
1
+ import { NmtsError } from "../errors.ts";
2
+ /**
3
+ * Where the code goes, or `null` for the screen.
4
+ *
5
+ * ⛔ `-` IS REFUSED, WHICH IS THE OPPOSITE OF WHAT IT MEANS EVERYWHERE ELSE IN THIS TOOL. In
6
+ * `get` and `listfile` it means "hand the bytes to whatever is reading stdout", and that is
7
+ * right for a file somebody already has. Here it would mean putting the only copy of an
8
+ * NMTS key into the same stream a program is parsing — which is the one place this command
9
+ * exists to keep it out of.
10
+ */
11
+ export declare function codeFileTarget(out: string | undefined): string | null;
12
+ /**
13
+ * Write the code where the caller pointed, readable by nobody else.
14
+ *
15
+ * ⚠ `wx` FAILS IF THE NAME APPEARED SINCE THE CHECK ABOVE, which is the point of using it rather
16
+ * than trusting that check: between the two, something else may have written there.
17
+ *
18
+ * ⚠ ON WINDOWS THE MODE IS IGNORED and the file inherits the folder's permissions — the same
19
+ * limit `credentials.ts` documents, and claiming otherwise would be claiming a guarantee the
20
+ * platform does not give.
21
+ */
22
+ export declare function writeCodeFile(path: string, code: string): void;
23
+ /** `--json` without `--out`: there is nowhere for the code to go that is not the output. */
24
+ export declare function jsonNeedsAFile(): NmtsError;
@@ -0,0 +1,93 @@
1
+ // Where a new NMTS key goes when it is not printed, and the rules around that file.
2
+ //
3
+ // ⛔ IT LEFT `create.ts` BECAUSE THERE ARE TWO PATHS ONTO IT NOW (2026-09-05). `create` makes an
4
+ // account either with a verified API key or through a registration address a person opens, and
5
+ // both make the same code and owe it the same treatment: written before anything is asked of
6
+ // the server, never overwritten, never sent to stdout. A second copy of these three rules is a
7
+ // second place for one of them to be relaxed.
8
+ //
9
+ // ⛔ AND THE FILE IS WRITTEN BEFORE THE ACCOUNT IS ASKED FOR. A full disk, a bad path or a name
10
+ // already taken must fail while there is still nothing to lose; discovering it AFTER the server
11
+ // has created the account would mean an account exists whose only key we are about to drop.
12
+ import { mkdirSync, statSync, writeFileSync } from "node:fs";
13
+ import { isAbsolute, resolve } from "node:path";
14
+ import { NmtsError } from "../errors.js";
15
+ import { STDOUT_TARGET } from "../stdout.js";
16
+ /**
17
+ * Where the code goes, or `null` for the screen.
18
+ *
19
+ * ⛔ `-` IS REFUSED, WHICH IS THE OPPOSITE OF WHAT IT MEANS EVERYWHERE ELSE IN THIS TOOL. In
20
+ * `get` and `listfile` it means "hand the bytes to whatever is reading stdout", and that is
21
+ * right for a file somebody already has. Here it would mean putting the only copy of an
22
+ * NMTS key into the same stream a program is parsing — which is the one place this command
23
+ * exists to keep it out of.
24
+ */
25
+ export function codeFileTarget(out) {
26
+ if (out === undefined || out === "")
27
+ return null;
28
+ if (out === STDOUT_TARGET) {
29
+ throw new NmtsError("The new NMTS key will not be sent to stdout.", {
30
+ exitCode: 2,
31
+ nextStep: `Nothing was created. stdout is what a program reads and a log keeps, and this is the ` +
32
+ `only copy of your NMTS key. Name a file — \`--out ./account-code.txt\` — or leave --out off ` +
33
+ `and read it off the screen.`,
34
+ });
35
+ }
36
+ const path = isAbsolute(out) ? out : resolve(process.cwd(), out);
37
+ let existing = null;
38
+ try {
39
+ existing = statSync(path);
40
+ }
41
+ catch {
42
+ // Not there is exactly what this wants.
43
+ }
44
+ if (existing !== null) {
45
+ // ⛔ NO `--force` HERE, DELIBERATELY. Everywhere else in this tool --force replaces a file
46
+ // that can be fetched again. The file this would replace may be the only copy of ANOTHER
47
+ // account's code, and overwriting it destroys that account with no way back.
48
+ throw new NmtsError(`${path} is already there.`, {
49
+ exitCode: 4,
50
+ nextStep: existing.isDirectory()
51
+ ? `--out names the FILE the NMTS key goes into, not a directory.`
52
+ : `Nothing was created. That file is not replaced, whatever --force says: it may hold ` +
53
+ `the only copy of another account's NMTS key. Name one that does not exist.`,
54
+ });
55
+ }
56
+ return path;
57
+ }
58
+ /**
59
+ * Write the code where the caller pointed, readable by nobody else.
60
+ *
61
+ * ⚠ `wx` FAILS IF THE NAME APPEARED SINCE THE CHECK ABOVE, which is the point of using it rather
62
+ * than trusting that check: between the two, something else may have written there.
63
+ *
64
+ * ⚠ ON WINDOWS THE MODE IS IGNORED and the file inherits the folder's permissions — the same
65
+ * limit `credentials.ts` documents, and claiming otherwise would be claiming a guarantee the
66
+ * platform does not give.
67
+ */
68
+ export function writeCodeFile(path, code) {
69
+ const dir = resolve(path, "..");
70
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
71
+ try {
72
+ writeFileSync(path, `${code}\n`, { mode: 0o600, flag: "wx" });
73
+ }
74
+ catch (error) {
75
+ // ⛔ THE CAUSE IS NAMED BUT THE CODE IS NOT. `writeFileSync`'s errno line carries the path and
76
+ // never the contents, so it is safe to pass on; the code itself appears in no message here.
77
+ throw new NmtsError(`The NMTS key could not be written to ${path}.`, {
78
+ exitCode: 1,
79
+ nextStep: `Nothing was created — the file is written before the account is asked for, so that a ` +
80
+ `failure here costs nothing. Cause: ${error instanceof Error ? error.message : String(error)}`,
81
+ });
82
+ }
83
+ }
84
+ /** `--json` without `--out`: there is nowhere for the code to go that is not the output. */
85
+ export function jsonNeedsAFile() {
86
+ return new NmtsError("--json needs --out, because the NMTS key will not go into the output.", {
87
+ exitCode: 2,
88
+ nextStep: `Nothing was created. Machine-readable output is read by a program and kept by a log, and ` +
89
+ `your NMTS key is the only key this account will ever have. \`--out ./account-code.txt\` writes ` +
90
+ `it to a file only you can read; the JSON then names that file. Without --json the NMTS key is ` +
91
+ `printed on the screen for a person to keep.`,
92
+ });
93
+ }
@@ -0,0 +1,10 @@
1
+ export interface CreateLinkOptions {
2
+ server: string;
3
+ network: string;
4
+ out?: string | undefined;
5
+ json?: boolean;
6
+ /** Print the address and stop, instead of waiting for somebody to use it. */
7
+ noWait?: boolean | undefined;
8
+ write?: ((line: string) => void) | undefined;
9
+ }
10
+ export declare function createThroughLink(options: CreateLinkOptions): Promise<number>;