@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.
- package/AGENTS.md +124 -516
- package/CHANGELOG.md +98 -0
- package/README.ko.md +374 -467
- package/README.md +384 -465
- package/dist/account-proof.d.ts +29 -0
- package/dist/account-proof.js +6 -6
- package/dist/account.d.ts +18 -0
- package/dist/account.js +5 -5
- package/dist/agent-host.d.ts +71 -0
- package/dist/agent-host.js +169 -0
- package/dist/api-advice.d.ts +2 -0
- package/dist/api-advice.js +210 -0
- package/dist/api-key.d.ts +79 -0
- package/dist/api-key.js +7 -7
- package/dist/api-refusal.d.ts +14 -0
- package/dist/api-refusal.js +8 -0
- package/dist/api.d.ts +114 -0
- package/dist/api.js +59 -162
- package/dist/args.d.ts +160 -0
- package/dist/args.js +118 -30
- package/dist/artifact-about.d.ts +49 -0
- package/dist/autonomy.d.ts +39 -0
- package/dist/autonomy.js +110 -32
- package/dist/code-access.d.ts +29 -0
- package/dist/code-access.js +3 -3
- package/dist/code-vault.d.ts +38 -0
- package/dist/code-vault.js +8 -8
- package/dist/collision.d.ts +67 -0
- package/dist/collision.js +1 -1
- package/dist/commands/accept-terms.d.ts +11 -0
- package/dist/commands/accept-terms.js +106 -0
- package/dist/commands/balance.d.ts +7 -0
- package/dist/commands/balance.js +54 -1
- package/dist/commands/create-code-file.d.ts +24 -0
- package/dist/commands/create-code-file.js +93 -0
- package/dist/commands/create-link.d.ts +10 -0
- package/dist/commands/create-link.js +232 -0
- package/dist/commands/create.d.ts +20 -0
- package/dist/commands/create.js +43 -102
- package/dist/commands/credits.d.ts +9 -0
- package/dist/commands/credits.js +110 -0
- package/dist/commands/delete-account.d.ts +11 -0
- package/dist/commands/delete-account.js +63 -0
- package/dist/commands/deposit.d.ts +7 -0
- package/dist/commands/deposit.js +85 -0
- package/dist/commands/devices.d.ts +12 -0
- package/dist/commands/devices.js +144 -0
- package/dist/commands/documents.d.ts +35 -0
- package/dist/commands/documents.js +212 -0
- package/dist/commands/env.d.ts +5 -0
- package/dist/commands/env.js +10 -2
- package/dist/commands/erase.d.ts +13 -0
- package/dist/commands/erase.js +193 -0
- package/dist/commands/expiring.d.ts +19 -0
- package/dist/commands/extend.d.ts +33 -0
- package/dist/commands/extend.js +54 -15
- package/dist/commands/get.d.ts +18 -0
- package/dist/commands/key-manage.d.ts +11 -0
- package/dist/commands/key-manage.js +162 -0
- package/dist/commands/key.d.ts +28 -0
- package/dist/commands/key.js +249 -0
- package/dist/commands/kit.d.ts +11 -0
- package/dist/commands/kit.js +7 -7
- package/dist/commands/listfile.d.ts +13 -0
- package/dist/commands/listfile.js +12 -5
- package/dist/commands/login.d.ts +18 -0
- package/dist/commands/login.js +19 -19
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +1 -1
- package/dist/commands/losses.d.ts +12 -0
- package/dist/commands/losses.js +180 -0
- package/dist/commands/ls.d.ts +23 -0
- package/dist/commands/marks-dispatch.d.ts +2 -0
- package/dist/commands/marks-dispatch.js +51 -0
- package/dist/commands/marks.d.ts +46 -0
- package/dist/commands/marks.js +80 -0
- package/dist/commands/mcp.d.ts +24 -0
- package/dist/commands/mcp.js +70 -5
- package/dist/commands/mode.d.ts +8 -0
- package/dist/commands/mode.js +60 -24
- package/dist/commands/on-collision.d.ts +6 -0
- package/dist/commands/organise.d.ts +46 -0
- package/dist/commands/padding.d.ts +7 -0
- package/dist/commands/padding.js +114 -0
- package/dist/commands/public-code.d.ts +9 -0
- package/dist/commands/public-code.js +8 -8
- package/dist/commands/pull.d.ts +27 -0
- package/dist/commands/push-wallet.d.ts +19 -0
- package/dist/commands/push-wallet.js +69 -0
- package/dist/commands/push.d.ts +47 -0
- package/dist/commands/push.js +37 -7
- package/dist/commands/put-wallet.d.ts +77 -0
- package/dist/commands/put-wallet.js +316 -0
- package/dist/commands/put.d.ts +56 -0
- package/dist/commands/put.js +53 -6
- package/dist/commands/rebuild.d.ts +18 -0
- package/dist/commands/rebuild.js +108 -10
- package/dist/commands/receive.d.ts +24 -0
- package/dist/commands/recovery-dispatch.d.ts +2 -0
- package/dist/commands/recovery-dispatch.js +34 -0
- package/dist/commands/recovery-list.d.ts +11 -0
- package/dist/commands/recovery-list.js +6 -6
- package/dist/commands/recovery.d.ts +25 -0
- package/dist/commands/recovery.js +2 -2
- package/dist/commands/rollback.d.ts +9 -0
- package/dist/commands/rollback.js +118 -0
- package/dist/commands/s3.d.ts +23 -0
- package/dist/commands/s3.js +6 -6
- package/dist/commands/settings.d.ts +5 -0
- package/dist/commands/settings.js +21 -6
- package/dist/commands/share-dispatch.d.ts +2 -0
- package/dist/commands/share-dispatch.js +46 -0
- package/dist/commands/share.d.ts +14 -0
- package/dist/commands/share.js +13 -3
- package/dist/commands/shares-sent.d.ts +18 -0
- package/dist/commands/shares-sent.js +95 -0
- package/dist/commands/support.d.ts +24 -0
- package/dist/commands/support.js +321 -0
- package/dist/commands/sweep.d.ts +11 -0
- package/dist/commands/tip.d.ts +11 -0
- package/dist/commands/tip.js +86 -0
- package/dist/commands/trash.d.ts +17 -0
- package/dist/commands/trash.js +1 -1
- package/dist/commands/trial.d.ts +7 -0
- package/dist/commands/trial.js +16 -18
- package/dist/commands/unlock.d.ts +16 -0
- package/dist/commands/unlock.js +159 -0
- package/dist/commands/update.d.ts +42 -0
- package/dist/commands/update.js +1 -1
- package/dist/commands/usage.d.ts +8 -0
- package/dist/commands/verify.d.ts +18 -0
- package/dist/commands/verify.js +10 -6
- package/dist/commands/wallet-activity.d.ts +26 -0
- package/dist/commands/wallet-activity.js +117 -0
- package/dist/commands/wallet-donate.d.ts +26 -0
- package/dist/commands/wallet-donate.js +163 -0
- package/dist/commands/wallet-hall.d.ts +35 -0
- package/dist/commands/wallet-hall.js +214 -0
- package/dist/commands/wallet-send.d.ts +22 -0
- package/dist/commands/wallet-send.js +153 -0
- package/dist/commands/wallet-storage-ops.d.ts +24 -0
- package/dist/commands/wallet-storage-ops.js +215 -0
- package/dist/commands/wallet-storage.d.ts +18 -0
- package/dist/commands/wallet-storage.js +95 -0
- package/dist/commands/wallet-swap.d.ts +32 -0
- package/dist/commands/wallet-swap.js +287 -0
- package/dist/commands/wallet.d.ts +36 -0
- package/dist/commands/wallet.js +57 -15
- package/dist/commands/whoami.d.ts +10 -0
- package/dist/commands/whoami.js +24 -6
- package/dist/consent.d.ts +140 -0
- package/dist/consent.js +89 -30
- package/dist/credentials.d.ts +146 -0
- package/dist/credentials.js +10 -10
- package/dist/crypto-surface.d.ts +197 -0
- package/dist/crypto.d.ts +81 -0
- package/dist/crypto.js +13 -0
- package/dist/deposit.d.ts +25 -0
- package/dist/deposit.js +78 -0
- package/dist/download-part.d.ts +40 -0
- package/dist/download-sink.d.ts +76 -0
- package/dist/download.d.ts +79 -0
- package/dist/download.js +3 -3
- package/dist/drive-paths.d.ts +73 -0
- package/dist/environment.d.ts +62 -0
- package/dist/environment.js +29 -9
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +3 -3
- package/dist/exit.d.ts +44 -0
- package/dist/expiry.d.ts +58 -0
- package/dist/extend-budget.d.ts +35 -0
- package/dist/extend-budget.js +81 -0
- package/dist/extend-chain.d.ts +43 -0
- package/dist/extend-chain.js +40 -3
- package/dist/extend-plan.d.ts +148 -0
- package/dist/extend-plan.js +1 -1
- package/dist/gate.d.ts +18 -0
- package/dist/gate.js +100 -0
- package/dist/guards.d.ts +2 -0
- package/dist/help.d.ts +20 -0
- package/dist/help.js +199 -33
- package/dist/human-check.d.ts +23 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +49 -0
- package/dist/item-trash.d.ts +1 -0
- package/dist/kit-file.d.ts +56 -0
- package/dist/kit-file.js +10 -10
- package/dist/list-file.d.ts +88 -0
- package/dist/list-file.js +90 -7
- package/dist/list-view-find.d.ts +15 -0
- package/dist/list-view-order.d.ts +32 -0
- package/dist/main.d.ts +12 -0
- package/dist/main.js +112 -113
- package/dist/manifest-chunk-cache.d.ts +14 -0
- package/dist/manifest-chunk-cache.js +103 -0
- package/dist/manifest-chunk-flow.d.ts +81 -0
- package/dist/manifest-chunk-flow.js +307 -0
- package/dist/manifest-create.d.ts +23 -0
- package/dist/manifest-create.js +26 -30
- package/dist/manifest-write.d.ts +161 -0
- package/dist/manifest-write.js +42 -33
- package/dist/manifest.d.ts +82 -0
- package/dist/manifest.js +69 -21
- package/dist/mark-render.d.ts +26 -0
- package/dist/mcp-args.d.ts +17 -0
- package/dist/mcp-ask.d.ts +43 -0
- package/dist/mcp-ask.js +100 -0
- package/dist/mcp-tools/context.d.ts +46 -0
- package/dist/mcp-tools/credits.d.ts +3 -0
- package/dist/mcp-tools/credits.js +61 -0
- package/dist/mcp-tools/files.d.ts +3 -0
- package/dist/mcp-tools/files.js +34 -0
- package/dist/mcp-tools/organise.d.ts +3 -0
- package/dist/mcp-tools/organise.js +82 -2
- package/dist/mcp-tools/reads.d.ts +3 -0
- package/dist/mcp-tools/reads.js +132 -4
- package/dist/mcp-tools/share.d.ts +3 -0
- package/dist/mcp-tools/share.js +26 -7
- package/dist/mcp-tools/support.d.ts +3 -0
- package/dist/mcp-tools/support.js +140 -0
- package/dist/mcp-tools/tiers.d.ts +31 -0
- package/dist/mcp-tools/tiers.js +147 -0
- package/dist/mcp.d.ts +80 -0
- package/dist/mcp.js +110 -9
- package/dist/net-retry.d.ts +38 -0
- package/dist/network.d.ts +10 -0
- package/dist/notice.d.ts +7 -0
- package/dist/notice.js +7 -7
- package/dist/product.d.ts +29 -0
- package/dist/product.js +1 -1
- package/dist/progress.d.ts +37 -0
- package/dist/prompt.d.ts +31 -0
- package/dist/prompt.js +1 -1
- package/dist/rebuild-key-check.d.ts +26 -0
- package/dist/rebuild-key-check.js +90 -0
- package/dist/rebuild.d.ts +87 -0
- package/dist/rebuild.js +26 -4
- package/dist/recovery-assemble.d.ts +40 -0
- package/dist/recovery-assemble.js +1 -1
- package/dist/recovery-build.d.ts +40 -0
- package/dist/recovery-map-file.d.ts +53 -0
- package/dist/recovery-map-file.js +4 -4
- package/dist/recovery-map.d.ts +197 -0
- package/dist/recovery-release.d.ts +104 -0
- package/dist/recovery-seq.d.ts +10 -0
- package/dist/recovery-source.d.ts +50 -0
- package/dist/redact.d.ts +70 -0
- package/dist/redact.js +212 -0
- package/dist/registration.d.ts +30 -0
- package/dist/registration.js +5 -5
- package/dist/risk.d.ts +380 -0
- package/dist/risk.js +206 -0
- package/dist/run-log.d.ts +78 -0
- package/dist/run-log.js +301 -0
- package/dist/s3/listing.d.ts +47 -0
- package/dist/s3/multipart.d.ts +21 -0
- package/dist/s3/response-sink.d.ts +8 -0
- package/dist/s3/same-file.d.ts +54 -0
- package/dist/s3/server.d.ts +58 -0
- package/dist/s3/server.js +2 -2
- package/dist/s3/sigv4.d.ts +46 -0
- package/dist/s3/sigv4.js +1 -1
- package/dist/s3/staging.d.ts +10 -0
- package/dist/s3/xml.d.ts +28 -0
- package/dist/safe-path.d.ts +41 -0
- package/dist/seal.d.ts +120 -0
- package/dist/secret-reader.d.ts +28 -0
- package/dist/server.d.ts +9 -0
- package/dist/session.d.ts +32 -0
- package/dist/session.js +1 -1
- package/dist/setup-questions.d.ts +7 -0
- package/dist/share.d.ts +108 -0
- package/dist/share.js +1 -1
- package/dist/shared/lib/crypto/size-padding.d.ts +55 -0
- package/dist/shared/lib/crypto/size-padding.js +5 -0
- package/dist/shared/lib/drive/manifest-chunks.d.ts +95 -0
- package/dist/shared/lib/drive/manifest-chunks.js +252 -0
- package/dist/shared/lib/drive/manifest-codec.d.ts +214 -0
- package/dist/shared/lib/drive/manifest-codec.js +7 -7
- package/dist/shared/lib/drive/manifest-index.d.ts +98 -0
- package/dist/shared/lib/drive/manifest-ops.d.ts +167 -0
- package/dist/shared/lib/drive/manifest-ops.js +9 -4
- package/dist/shared/lib/drive/manifest-pack.d.ts +65 -0
- package/dist/shared/lib/drive/manifest-pack.js +287 -0
- package/dist/shared/lib/drive/manifest-settings.d.ts +113 -0
- package/dist/shared/lib/drive/manifest-settings.js +87 -6
- package/dist/shared/lib/drive/name-conflict.d.ts +76 -0
- package/dist/shared/lib/drive/rebuild-verify.d.ts +73 -0
- package/dist/shared/lib/drive/rebuild-verify.js +112 -0
- package/dist/shared/lib/drive/unique-name.d.ts +9 -0
- package/dist/shared/lib/drive/zstd.d.ts +27 -0
- package/dist/shared/lib/drive/zstd.js +74 -0
- package/dist/shared/lib/extend/epochs.d.ts +205 -0
- package/dist/shared/lib/net/retry-budget.d.ts +79 -0
- package/dist/shared/lib/share/shared-file-info.d.ts +21 -0
- package/dist/shared/lib/storage-control/chain.d.ts +57 -0
- package/dist/shared/lib/storage-control/chain.js +82 -0
- package/dist/shared/lib/storage-control/plan.d.ts +40 -0
- package/dist/shared/lib/storage-control/plan.js +72 -0
- package/dist/shared/lib/storage-control/reuse.d.ts +72 -0
- package/dist/shared/lib/storage-control/reuse.js +95 -0
- package/dist/shared/lib/storage-network.d.ts +25 -0
- package/dist/shared/lib/upload/part-plan.d.ts +24 -0
- package/dist/shared/lib/wallet/activity.d.ts +138 -0
- package/dist/shared/lib/wallet/activity.js +208 -0
- package/dist/shared/lib/wallet/send-rules.d.ts +68 -0
- package/dist/shared/lib/wallet/send-rules.js +116 -0
- package/dist/shared/lib/wallet/swap-rules.d.ts +115 -0
- package/dist/shared/lib/wallet/swap-rules.js +199 -0
- package/dist/shared/lib/wallet/tip.d.ts +14 -0
- package/dist/shared/lib/wallet/tip.js +34 -0
- package/dist/shared/lib/wallet/venue-ids.d.ts +23 -0
- package/dist/shared/lib/wallet/venue-ids.js +53 -0
- package/dist/standing-tip.d.ts +24 -0
- package/dist/standing-tip.js +52 -0
- package/dist/stdout.d.ts +38 -0
- package/dist/storage-control-chain.d.ts +38 -0
- package/dist/storage-control-chain.js +78 -0
- package/dist/sui-rpc.d.ts +15 -0
- package/dist/sui-rpc.js +59 -0
- package/dist/support-attach.d.ts +24 -0
- package/dist/support-attach.js +72 -0
- package/dist/support-copy.d.ts +42 -0
- package/dist/support-copy.js +109 -0
- package/dist/support-wire.d.ts +49 -0
- package/dist/support-wire.js +194 -0
- package/dist/trash-sweep.d.ts +55 -0
- package/dist/units.d.ts +2 -0
- package/dist/update-check.d.ts +50 -0
- package/dist/update-source.d.ts +63 -0
- package/dist/upload-api.d.ts +3 -0
- package/dist/upload-file.d.ts +97 -0
- package/dist/upload-file.js +3 -2
- package/dist/upload-price.d.ts +50 -0
- package/dist/upload-steps.d.ts +26 -0
- package/dist/upload-steps.js +29 -9
- package/dist/upload-store.d.ts +182 -0
- package/dist/upload-wallet-chain.d.ts +54 -0
- package/dist/upload-wallet-chain.js +203 -0
- package/dist/upload-wallet-plan.d.ts +128 -0
- package/dist/upload-wallet-plan.js +184 -0
- package/dist/upload-wallet.d.ts +24 -0
- package/dist/upload-wallet.js +208 -0
- package/dist/upload-wire.d.ts +244 -0
- package/dist/upload.d.ts +18 -0
- package/dist/upload.js +10 -0
- package/dist/usage-report.d.ts +35 -0
- package/dist/wallet-activity-chain.d.ts +5 -0
- package/dist/wallet-activity-chain.js +31 -0
- package/dist/wallet-chain.d.ts +19 -0
- package/dist/wallet-chain.js +2 -2
- package/dist/wallet-grant.d.ts +50 -0
- package/dist/wallet-grant.js +218 -0
- package/dist/wallet-send-chain.d.ts +24 -0
- package/dist/wallet-send-chain.js +49 -0
- package/dist/wallet-sign.d.ts +133 -0
- package/dist/wallet-sign.js +317 -0
- package/dist/wallet-storage-chain.d.ts +3 -0
- package/dist/wallet-storage-chain.js +20 -0
- package/dist/wallet-swap-chain.d.ts +52 -0
- package/dist/wallet-swap-chain.js +186 -0
- package/dist/wallet-swap-quote.d.ts +22 -0
- package/dist/wallet-swap-quote.js +126 -0
- package/dist/wallet.d.ts +135 -0
- package/dist/wallet.js +5 -5
- package/dist/walrus-write.d.ts +56 -0
- package/dist/walrus-write.js +5 -4
- package/dist/walrus.d.ts +86 -0
- package/dist/walrus.js +48 -10
- package/dist/zstd-node.d.ts +11 -0
- package/dist/zstd-node.js +71 -0
- package/docs/commands/accept-terms.md +15 -0
- package/docs/commands/create.md +17 -0
- package/docs/commands/credits.md +34 -0
- package/docs/commands/delete-account.md +14 -0
- package/docs/commands/deposit.md +27 -0
- package/docs/commands/devices.md +9 -0
- package/docs/commands/documents.md +10 -0
- package/docs/commands/env.md +21 -0
- package/docs/commands/extend.md +27 -0
- package/docs/commands/folders.md +10 -0
- package/docs/commands/get.md +14 -0
- package/docs/commands/key.md +14 -0
- package/docs/commands/login.md +23 -0
- package/docs/commands/logout.md +11 -0
- package/docs/commands/losses.md +13 -0
- package/docs/commands/ls.md +16 -0
- package/docs/commands/marks.md +9 -0
- package/docs/commands/mcp.md +33 -0
- package/docs/commands/mode.md +29 -0
- package/docs/commands/on-collision.md +13 -0
- package/docs/commands/padding.md +13 -0
- package/docs/commands/public-code.md +9 -0
- package/docs/commands/pull.md +9 -0
- package/docs/commands/push.md +12 -0
- package/docs/commands/put.md +36 -0
- package/docs/commands/rebuild.md +13 -0
- package/docs/commands/recovery.md +18 -0
- package/docs/commands/s3.md +17 -0
- package/docs/commands/share.md +15 -0
- package/docs/commands/support.md +16 -0
- package/docs/commands/tip.md +21 -0
- package/docs/commands/trash.md +25 -0
- package/docs/commands/trial.md +12 -0
- package/docs/commands/unlock.md +25 -0
- package/docs/commands/update.md +14 -0
- package/docs/commands/usage.md +14 -0
- package/docs/commands/verify.md +18 -0
- package/docs/commands/wallet.md +67 -0
- package/docs/commands/whoami.md +12 -0
- package/package.json +67 -8
- package/server.json +28 -0
- package/vendor/nmts-crypto/nmts_crypto_wasm.d.ts +23 -2
- package/vendor/nmts-crypto/nmts_crypto_wasm.js +44 -2
- package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm +0 -0
- package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm.d.ts +1 -0
- package/vendor/nmts-crypto/package.json +1 -1
- package/dist/commands/consent.js +0 -77
- 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>;
|
package/dist/code-access.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Getting the
|
|
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
|
|
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
|
|
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;
|
package/dist/code-vault.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// Locking the
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 === "
|
|
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
|
+
}
|
package/dist/commands/balance.js
CHANGED
|
@@ -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
|
|
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>;
|