@needmoretruth/nmts-cli 0.17.2 → 0.34.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +125 -517
- package/CHANGELOG.md +104 -0
- package/README.ko.md +377 -469
- package/README.md +394 -474
- 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,40 @@
|
|
|
1
|
+
import type { CryptoGlue } from "./crypto.ts";
|
|
2
|
+
import { type ReadOptions } from "./walrus.ts";
|
|
3
|
+
export interface PartView {
|
|
4
|
+
part_index: number;
|
|
5
|
+
storage_kind: number;
|
|
6
|
+
network?: number;
|
|
7
|
+
blob_id: string;
|
|
8
|
+
patch_id?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PartsResponse {
|
|
11
|
+
size: number;
|
|
12
|
+
parts: PartView[];
|
|
13
|
+
}
|
|
14
|
+
export declare function asParts(value: unknown): PartsResponse;
|
|
15
|
+
/**
|
|
16
|
+
* Fetch one part's sealed bytes.
|
|
17
|
+
*
|
|
18
|
+
* ⛔ Refuse before reading, not after. A part on a storage network this build has no reader for
|
|
19
|
+
* would otherwise be fetched from a Walrus aggregator, 404, and be reported as missing bytes —
|
|
20
|
+
* which is a different thing and sends somebody looking for the wrong one.
|
|
21
|
+
*/
|
|
22
|
+
export declare function fetchPart(part: PartView, chain: string, read: ReadOptions | undefined): Promise<Uint8Array>;
|
|
23
|
+
/**
|
|
24
|
+
* Open ONE part and pass its contribution on, a chunk at a time. Returns how much of the file it
|
|
25
|
+
* contributed.
|
|
26
|
+
*
|
|
27
|
+
* ⛔ THE SEALED BYTES ARE FED IN ONE CHUNK AT A TIME, not all at once. Handing the engine the whole
|
|
28
|
+
* part would make it hand back the whole part's plaintext in one array, which is the ceiling
|
|
29
|
+
* this path exists to remove; feeding it a chunk's worth means at most one chunk of plaintext
|
|
30
|
+
* exists at a time. The size fed is the format's own chunk plus its tag, so a well-formed
|
|
31
|
+
* stream yields exactly one chunk per push — and a stream whose header declares a different
|
|
32
|
+
* chunk size still works, because the engine buffers what it has not finished.
|
|
33
|
+
*
|
|
34
|
+
* ⛔ `finish()` IS WHAT CATCHES A PART CUT SHORT. Every chunk that arrived authenticates; only the
|
|
35
|
+
* end-of-stream check knows the rest is missing. Skipping it would accept a truncated part.
|
|
36
|
+
*
|
|
37
|
+
* ⛔ THE ENGINE-SIDE SESSION IS FREED ON EVERY PATH OUT, including a failure: it holds the file
|
|
38
|
+
* key until it is, and a download that failed is exactly when nobody comes back to tidy up.
|
|
39
|
+
*/
|
|
40
|
+
export declare function openPart(crypt: CryptoGlue, dek: Uint8Array, part: PartView, sealed: Uint8Array, isLast: boolean, remaining: number, emit: (body: Uint8Array) => Promise<void>): Promise<number>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type ByteDestination } from "./stdout.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Somewhere a file's plaintext is delivered in order, and made visible only once.
|
|
4
|
+
*
|
|
5
|
+
* The shape is three-part on purpose: `write` moves bytes, `commit` is the only thing that may
|
|
6
|
+
* make them readable under the name somebody asked for, and `abandon` must leave nothing behind.
|
|
7
|
+
* A destination that cannot separate those three cannot hold the integrity promise above.
|
|
8
|
+
*/
|
|
9
|
+
export interface PlaintextSink {
|
|
10
|
+
/**
|
|
11
|
+
* The file's real length, from the sealed list, BEFORE a single stored part is fetched.
|
|
12
|
+
*
|
|
13
|
+
* ⛔ A destination that cannot take a file this size refuses HERE, where nothing has been read
|
|
14
|
+
* from the network and nothing has been written. Discovering it half way through means the
|
|
15
|
+
* refusal costs a download that was never going to be delivered.
|
|
16
|
+
*/
|
|
17
|
+
expect(size: number): void;
|
|
18
|
+
/** Take the next run of plaintext, in order. Resolves when the bytes are no longer needed. */
|
|
19
|
+
write(bytes: Uint8Array): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Everything checked: make the file visible, or hand it over.
|
|
22
|
+
*
|
|
23
|
+
* False means the reader closed the pipe before the file was done — see `handOver`. Every other
|
|
24
|
+
* failure throws: a caller must not be able to mistake "it stopped half way" for "delivered".
|
|
25
|
+
*/
|
|
26
|
+
commit(): Promise<boolean>;
|
|
27
|
+
/** Something did not check out: leave nothing behind. Never throws. */
|
|
28
|
+
abandon(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Write to a file, through a temporary name beside it, renamed into place only when the whole
|
|
32
|
+
* file has been proved.
|
|
33
|
+
*
|
|
34
|
+
* ⛔ THE TEMPORARY FILE IS IN THE SAME DIRECTORY AS THE DESTINATION, not in a system temporary
|
|
35
|
+
* directory, and that is the whole mechanism: `rename` is atomic only within one filesystem,
|
|
36
|
+
* and only an atomic rename guarantees that a reader either sees nothing under the real name or
|
|
37
|
+
* sees the finished file. A temporary directory can be — and on the machines this runs on
|
|
38
|
+
* usually is — a different filesystem, where the same call becomes copy-then-delete and a
|
|
39
|
+
* reader can catch the copy half done.
|
|
40
|
+
*
|
|
41
|
+
* ⛔ IT IS CREATED 0600 AND THE RENAME KEEPS THAT. The file is somebody's plaintext; it must not
|
|
42
|
+
* be readable by other accounts on the machine for the seconds it takes to download, any more
|
|
43
|
+
* than it may be afterwards.
|
|
44
|
+
*/
|
|
45
|
+
export declare function fileSink(destination: string, options: {
|
|
46
|
+
force: boolean;
|
|
47
|
+
}): PlaintextSink;
|
|
48
|
+
/**
|
|
49
|
+
* How much plaintext `--out -` will hold before it hands anything over. One part's worth.
|
|
50
|
+
*
|
|
51
|
+
* ⛔ NOT AN ARBITRARY NUMBER: it is the upload path's default part size, which is the most memory
|
|
52
|
+
* this tool has ever asked for. Keeping the stdout ceiling there means the whole tool's bound
|
|
53
|
+
* is one part plus one chunk whichever direction the bytes are going.
|
|
54
|
+
*/
|
|
55
|
+
export declare const STDOUT_HOLD_LIMIT: number;
|
|
56
|
+
/**
|
|
57
|
+
* Hand the whole file to whatever is reading stdout — after it has been proved, not during.
|
|
58
|
+
*
|
|
59
|
+
* ⛔ THERE IS NO RENAME ON A PIPE, so the trick the file path uses is not available: a byte handed
|
|
60
|
+
* to a reader cannot be taken back, and a reader that has already consumed half a file cannot
|
|
61
|
+
* be told afterwards that the half was wrong. An exit code does not fix that — `nmts get x
|
|
62
|
+
* --out - > y` leaves the truncated bytes in `y` whatever the exit code says, and an agent
|
|
63
|
+
* reading a pipe usually acts on what it read. So this branch keeps the ORIGINAL guarantee
|
|
64
|
+
* rather than the original size: the file is held, checked, and only then handed over, exactly
|
|
65
|
+
* as before streaming existed.
|
|
66
|
+
*
|
|
67
|
+
* ⛔ WHICH MEANS IT HAS TO REFUSE SOMEWHERE, AND IT REFUSES OUT LOUD AND EARLY. Above the ceiling
|
|
68
|
+
* the answer is a refusal naming `--out <name>`, which streams and is checked before the file
|
|
69
|
+
* appears — not a silent trade of the guarantee for the size, and not the out-of-memory crash
|
|
70
|
+
* that this used to be. `put` already refuses this way rather than discovering a limit halfway.
|
|
71
|
+
*
|
|
72
|
+
* ⚠ Every protection this branch already had is still here and still applies to the whole file:
|
|
73
|
+
* bytes a terminal would act on are refused (`readableOnATerminal`), a reader that closed the
|
|
74
|
+
* pipe first is an ordinary end and not a failure, and any other write failure throws.
|
|
75
|
+
*/
|
|
76
|
+
export declare function stdoutSink(to: ByteDestination, limit?: number): PlaintextSink;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { PlaintextSink } from "./download-sink.ts";
|
|
2
|
+
import type { ReadOptions } from "./walrus.ts";
|
|
3
|
+
export interface FetchedFile {
|
|
4
|
+
/**
|
|
5
|
+
* How many plaintext bytes were delivered — the file's real length.
|
|
6
|
+
*
|
|
7
|
+
* ⚠ A COUNT, NOT THE BYTES. There is deliberately nothing here to read the file out of: the
|
|
8
|
+
* plaintext went to the sink as it was produced and was zeroed behind it, and a field holding
|
|
9
|
+
* it would put the ceiling this module exists to remove straight back.
|
|
10
|
+
*/
|
|
11
|
+
byteCount: number;
|
|
12
|
+
/** How many stored objects it came from. */
|
|
13
|
+
partCount: number;
|
|
14
|
+
/** Whether the file's own sealed hash was there to check against, and matched. */
|
|
15
|
+
contentHashChecked: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the whole file reached its destination.
|
|
18
|
+
*
|
|
19
|
+
* False has exactly one meaning: the program reading `--out -` closed the pipe before the file
|
|
20
|
+
* was done, which is an ordinary end and not a failure (`handOver`). Anything else throws.
|
|
21
|
+
*/
|
|
22
|
+
delivered: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface FetchInput {
|
|
25
|
+
base: string;
|
|
26
|
+
apiKey: string;
|
|
27
|
+
accountCode: string;
|
|
28
|
+
itemId: string;
|
|
29
|
+
/** The file's real length, from the account's sealed file list. */
|
|
30
|
+
size: number;
|
|
31
|
+
/** Wrapped file key from the sealed file list. Without it nothing can be opened. */
|
|
32
|
+
dekWrapped: string;
|
|
33
|
+
/** Sealed whole-file hash from the sealed file list, when the account recorded one. */
|
|
34
|
+
contentHashCt?: string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Which CHAIN this account's storage lives on — `mainnet` or `testnet`.
|
|
37
|
+
*
|
|
38
|
+
* ⛔ NOT the same question as a part's `network` field. That one says which STORAGE NETWORK holds
|
|
39
|
+
* the bytes (Walrus, and so far only Walrus); this one picks which of that network's
|
|
40
|
+
* aggregators to ask, because a blob id is meaningful on exactly one chain.
|
|
41
|
+
*/
|
|
42
|
+
chain: string;
|
|
43
|
+
read?: ReadOptions;
|
|
44
|
+
/** Where the plaintext goes as it is decrypted. Committed only after the whole file checks out. */
|
|
45
|
+
sink: PlaintextSink;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Fetch, decrypt and verify one file.
|
|
49
|
+
*
|
|
50
|
+
* The NMTS key is used and not kept: the data key is derived, unwrapped keys are zeroed, and
|
|
51
|
+
* the derivation output — which holds every other key in the account — never outlives this call.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* Fetch, decrypt and verify one file whose key is ALREADY OPEN.
|
|
55
|
+
*
|
|
56
|
+
* ⛔ SPLIT OUT BECAUSE THERE ARE TWO WAYS TO GET THAT KEY, and only one of them belongs to the
|
|
57
|
+
* account holding it. A file this account owns has its key wrapped in its own sealed list; a
|
|
58
|
+
* file somebody SHARED has its key inside an envelope only this account can open, sealed under
|
|
59
|
+
* a different separator, and its real length comes from what the sender sealed rather than from
|
|
60
|
+
* the account's own list. Everything after the key is identical — and writing it twice is how
|
|
61
|
+
* one copy comes to check the hash and the other does not.
|
|
62
|
+
*/
|
|
63
|
+
export declare function fetchWithKey(input: {
|
|
64
|
+
base: string;
|
|
65
|
+
apiKey: string;
|
|
66
|
+
/** Where the server describes the stored parts. Different for an owned and a shared file. */
|
|
67
|
+
descriptorPath: string;
|
|
68
|
+
/** The file's REAL plaintext length. */
|
|
69
|
+
size: number;
|
|
70
|
+
/** The file's own key, already unwrapped. Wiped here. */
|
|
71
|
+
dek: Uint8Array;
|
|
72
|
+
/** The whole-plaintext digest to check against, or null when none was recorded. */
|
|
73
|
+
expected: Uint8Array | null;
|
|
74
|
+
chain: string;
|
|
75
|
+
read?: ReadOptions;
|
|
76
|
+
/** Where the plaintext goes as it is decrypted. Committed only after the whole file checks out. */
|
|
77
|
+
sink: PlaintextSink;
|
|
78
|
+
}): Promise<FetchedFile>;
|
|
79
|
+
export declare function fetchFile(input: FetchInput): Promise<FetchedFile>;
|
package/dist/download.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// ⛔ THE SERVER NEVER SEES THE FILE, AND NOTHING HERE TRUSTS IT WITH ONE. It says which stored
|
|
4
4
|
// objects a file is made of and how big the file is; every byte that becomes the file is
|
|
5
|
-
// decrypted here under a key derived from the
|
|
5
|
+
// decrypted here under a key derived from the NMTS key, and the whole thing is checked
|
|
6
6
|
// against a hash the account sealed for itself. A server that lies about the parts, or an
|
|
7
7
|
// aggregator that returns the wrong bytes, produces a refusal — never a quietly wrong file.
|
|
8
8
|
//
|
|
@@ -34,7 +34,7 @@ import { NmtsError } from "./errors.js";
|
|
|
34
34
|
/**
|
|
35
35
|
* Fetch, decrypt and verify one file.
|
|
36
36
|
*
|
|
37
|
-
* The
|
|
37
|
+
* The NMTS key is used and not kept: the data key is derived, unwrapped keys are zeroed, and
|
|
38
38
|
* the derivation output — which holds every other key in the account — never outlives this call.
|
|
39
39
|
*/
|
|
40
40
|
/**
|
|
@@ -74,7 +74,7 @@ export async function fetchFile(input) {
|
|
|
74
74
|
catch {
|
|
75
75
|
dataKey.fill(0);
|
|
76
76
|
throw new NmtsError("This file's key did not open with this account's key.", {
|
|
77
|
-
nextStep: "Either the
|
|
77
|
+
nextStep: "Either the NMTS key belongs to somebody else, or the file list has been altered.",
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
let expected = null;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ManifestEntry } from "./shared/lib/drive/manifest-codec.ts";
|
|
2
|
+
import { buildIndex, isLive, type ManifestIndex, trashedAt } from "./shared/lib/drive/manifest-index.ts";
|
|
3
|
+
/** Folder. The same numeric codes the items API uses. */
|
|
4
|
+
export declare const KIND_FOLDER = 0;
|
|
5
|
+
/** File. */
|
|
6
|
+
export declare const KIND_FILE = 1;
|
|
7
|
+
export { buildIndex, isLive, trashedAt };
|
|
8
|
+
export type { ManifestIndex };
|
|
9
|
+
/**
|
|
10
|
+
* The full path of one entry, marked when the walk could not reach the root.
|
|
11
|
+
*
|
|
12
|
+
* ⚠ The `seen` set is not defensive tidiness: a list where two folders are each other's parent
|
|
13
|
+
* would loop forever, and a list is a file that can arrive from anywhere.
|
|
14
|
+
*/
|
|
15
|
+
export declare function fullPathOf(index: ManifestIndex, entry: ManifestEntry): string;
|
|
16
|
+
/**
|
|
17
|
+
* `/photos/2026/` and `./photos/2026` and `photos/2026` are one path.
|
|
18
|
+
*
|
|
19
|
+
* ⛔ AND SO ARE THE TWO SPELLINGS OF `café`. Unicode gives the same visible name more than one
|
|
20
|
+
* byte sequence — macOS hands back the decomposed form from the shell and the filesystem while
|
|
21
|
+
* a browser typically wrote the composed one. Comparing raw bytes meant `nmts rm café` could
|
|
22
|
+
* address a DIFFERENT entry from the one on screen. Both sides of every comparison here are
|
|
23
|
+
* folded to one form; what gets STORED is untouched, because the name belongs to whoever wrote
|
|
24
|
+
* it.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Strip a folder's own path off one of its descendants, in DRIVE terms.
|
|
28
|
+
*
|
|
29
|
+
* ⛔ IT IS STRING ARITHMETIC AND NOT `node:path`. A drive path always uses `/`, whatever separator
|
|
30
|
+
* the machine reading it happens to use, and `path.relative` answers in the MACHINE's
|
|
31
|
+
* separator. On Windows that turned `deep/under.txt` into `deep\under.txt`, which the
|
|
32
|
+
* containment check downstream then refused as a name trying to leave its directory — so
|
|
33
|
+
* fetching a folder failed outright on one of the three platforms this tool ships for, and
|
|
34
|
+
* every test passed, because on the other two the two separators are the same character.
|
|
35
|
+
* ⚠ That is the whole class: a drive path and a path on this disk are different kinds of thing,
|
|
36
|
+
* and `node:path` is only ever right about the second.
|
|
37
|
+
*
|
|
38
|
+
* A path that is not under the prefix comes back unchanged — the caller decides what that means.
|
|
39
|
+
*/
|
|
40
|
+
export declare function underPrefix(prefix: string, drivePath: string): string;
|
|
41
|
+
export declare function normalisePath(input: string): string;
|
|
42
|
+
/** The same folding, for one name rather than a path. */
|
|
43
|
+
export declare function normaliseName(name: string): string;
|
|
44
|
+
export interface FindOptions {
|
|
45
|
+
/** Include entries in the trash. Off by default — `rm` twice must not find its own work. */
|
|
46
|
+
includeTrashed?: boolean;
|
|
47
|
+
/** Only entries of this kind. */
|
|
48
|
+
kind?: number;
|
|
49
|
+
/** What the caller is about to do, for the refusal's second line. */
|
|
50
|
+
nothingHappened?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The one entry at this path, or a refusal saying which of the two ways it failed.
|
|
54
|
+
*
|
|
55
|
+
* ⛔ EXIT CODE 4, NOT 1: the command exists and could not do it, which is a different thing from
|
|
56
|
+
* the command being wrong. An agent is told to stop rather than to retry.
|
|
57
|
+
*/
|
|
58
|
+
export declare function entryAt(entries: readonly ManifestEntry[], path: string, options?: FindOptions): ManifestEntry;
|
|
59
|
+
/**
|
|
60
|
+
* The folder id a destination names, or null for the top of the drive.
|
|
61
|
+
*
|
|
62
|
+
* ⚠ An empty destination is the ROOT, and that is not the same as "no destination given" being an
|
|
63
|
+
* error: `--to ""` and `--to /` both mean the top, which is what somebody types to move
|
|
64
|
+
* something back out of a folder.
|
|
65
|
+
*/
|
|
66
|
+
export declare function folderIdFor(wanted: string | undefined, entries: readonly ManifestEntry[], nothingHappened?: string): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* The names already used in one folder — what a new or renamed entry must not collide with.
|
|
69
|
+
*
|
|
70
|
+
* ⚠ Folded the same way paths are, so the two spellings of one visible name count as one taken
|
|
71
|
+
* name. Two entries a person cannot tell apart are worse than a refusal they can act on.
|
|
72
|
+
*/
|
|
73
|
+
export declare function namesIn(entries: readonly ManifestEntry[], parentId: string | null): Set<string>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type HostSighting } from "./agent-host.ts";
|
|
2
|
+
/** How this process is contained, as far as it can tell. */
|
|
3
|
+
export type Containment = "docker" | "podman" | "container" | "none" | "unknown";
|
|
4
|
+
export interface Environment {
|
|
5
|
+
/** `linux`, `darwin`, `win32`, … — what Node reports, not a guess. */
|
|
6
|
+
os: NodeJS.Platform;
|
|
7
|
+
osRelease: string;
|
|
8
|
+
/** Node's own version. An agent choosing between behaviours may care. */
|
|
9
|
+
node: string;
|
|
10
|
+
containment: Containment;
|
|
11
|
+
/**
|
|
12
|
+
* Is root here somebody else on the host?
|
|
13
|
+
*
|
|
14
|
+
* ⚠ NOT THE SAME QUESTION AS `uid`. A rootless container is uid 0 inside and an ordinary user
|
|
15
|
+
* outside; a rootful one is root in both places. `null` where it cannot be measured.
|
|
16
|
+
*/
|
|
17
|
+
rootMapped: boolean | null;
|
|
18
|
+
/** The effective user id INSIDE this process's namespace, where the platform has one. */
|
|
19
|
+
uid: number | null;
|
|
20
|
+
/** Can a file here be kept private? MEASURED — see `codeStorageIsPrivate`. */
|
|
21
|
+
privateStorage: boolean;
|
|
22
|
+
/** Where anything this tool keeps would go. */
|
|
23
|
+
configDir: string;
|
|
24
|
+
/** Is there a person at a keyboard? A prompt is impossible without one. */
|
|
25
|
+
interactive: boolean;
|
|
26
|
+
/** Could a browser be opened here — needed for anything behind a human check. */
|
|
27
|
+
browserReachable: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Which agent hosts left a marker in this environment.
|
|
30
|
+
*
|
|
31
|
+
* ⚠ EVERY ONE OF THESE IS AN ANCESTOR, NOT NECESSARILY THE PARENT — markers are inherited, so a
|
|
32
|
+
* tool started by an agent that was itself started by another agent sees both. The direct
|
|
33
|
+
* parent is only knowable over the protocol, and that lives in the MCP server, not here.
|
|
34
|
+
* Empty means no marker was found, which includes every host that clears the environment.
|
|
35
|
+
*/
|
|
36
|
+
agentHosts: HostSighting[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Is this a container, and which kind?
|
|
40
|
+
*
|
|
41
|
+
* The three signals, in order of how much they actually prove:
|
|
42
|
+
* · `/run/.containerenv` — Podman writes it, and it names its own settings inside.
|
|
43
|
+
* · `/.dockerenv` — Docker writes it and has for a decade.
|
|
44
|
+
* · the process's own cgroup line naming a runtime.
|
|
45
|
+
* On a platform where none of those files can exist the answer is `unknown`, not `none`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function detectContainment(): Containment;
|
|
48
|
+
export declare function readEnvironment(): Environment;
|
|
49
|
+
/** One fact worth acting on, and what to do about it. */
|
|
50
|
+
export interface Advice {
|
|
51
|
+
/** `warn` is something to tell the person about. `note` is context. */
|
|
52
|
+
level: "warn" | "note";
|
|
53
|
+
text: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* What this environment means, in the terms somebody has to decide in.
|
|
57
|
+
*
|
|
58
|
+
* ⛔ WRITTEN FOR AN AGENT TO REPEAT TO A PERSON. Each line is a complete sentence about a fact
|
|
59
|
+
* that was measured here, so passing it along loses nothing. None of it is an instruction
|
|
60
|
+
* aimed past the reader.
|
|
61
|
+
*/
|
|
62
|
+
export declare function adviseFor(env: Environment, hasStoredCode: boolean): Advice[];
|
package/dist/environment.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Working out where this tool is running, and what that means for the
|
|
1
|
+
// Working out where this tool is running, and what that means for the NMTS key.
|
|
2
2
|
//
|
|
3
3
|
// ⛔ IT IS FOR THE AGENT, AND THE AGENT IS FOR THE PERSON. A program driving this tool cannot see
|
|
4
4
|
// what a person can — whether it is inside a container, whether the home directory survives the
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
import { existsSync, readFileSync } from "node:fs";
|
|
17
17
|
import { platform, release, tmpdir, userInfo } from "node:os";
|
|
18
18
|
import { configDir, codeStorageIsPrivate, modesAreEnforced } from "./credentials.js";
|
|
19
|
+
import { HOST_NAMES, hostsInEnvironment, washingHosts } from "./agent-host.js";
|
|
20
|
+
import { BINARY_NAME } from "./product.js";
|
|
19
21
|
/**
|
|
20
22
|
* Is this a container, and which kind?
|
|
21
23
|
*
|
|
@@ -104,6 +106,7 @@ export function readEnvironment() {
|
|
|
104
106
|
configDir: configDir(),
|
|
105
107
|
interactive: process.stdin.isTTY === true,
|
|
106
108
|
browserReachable: canOpenBrowser(),
|
|
109
|
+
agentHosts: hostsInEnvironment(),
|
|
107
110
|
};
|
|
108
111
|
}
|
|
109
112
|
/**
|
|
@@ -118,8 +121,8 @@ export function adviseFor(env, hasStoredCode) {
|
|
|
118
121
|
if (!env.privateStorage) {
|
|
119
122
|
out.push({
|
|
120
123
|
level: "warn",
|
|
121
|
-
text: `This filesystem does not keep the mode a file is written with, so a stored
|
|
122
|
-
`could be read by anything else that can reach ${env.configDir}. Supplying the
|
|
124
|
+
text: `This filesystem does not keep the mode a file is written with, so a stored NMTS key ` +
|
|
125
|
+
`could be read by anything else that can reach ${env.configDir}. Supplying the NMTS key for ` +
|
|
123
126
|
`each run instead of storing it avoids that.`,
|
|
124
127
|
});
|
|
125
128
|
}
|
|
@@ -127,13 +130,13 @@ export function adviseFor(env, hasStoredCode) {
|
|
|
127
130
|
out.push({
|
|
128
131
|
level: "note",
|
|
129
132
|
text: `This is a ${env.containment} container. Anything written to ${env.configDir} is lost when ` +
|
|
130
|
-
`it is removed unless that path is a volume, so a stored
|
|
133
|
+
`it is removed unless that path is a volume, so a stored NMTS key will not be there ` +
|
|
131
134
|
`next time.`,
|
|
132
135
|
});
|
|
133
136
|
out.push({
|
|
134
137
|
level: "warn",
|
|
135
|
-
text: `Do not pass the
|
|
136
|
-
`environment is visible to anybody who can inspect it. Put the
|
|
138
|
+
text: `Do not pass the NMTS key as an environment variable in a container: the whole ` +
|
|
139
|
+
`environment is visible to anybody who can inspect it. Put the NMTS key in a file and name ` +
|
|
137
140
|
`that file in NMTS_ACCOUNT_CODE_FILE, or pipe it in.`,
|
|
138
141
|
});
|
|
139
142
|
// ⛔ TWO DIFFERENT FACTS, and conflating them is how "rootless" gets reported backwards. What
|
|
@@ -162,7 +165,7 @@ export function adviseFor(env, hasStoredCode) {
|
|
|
162
165
|
if (!env.interactive && !hasStoredCode) {
|
|
163
166
|
out.push({
|
|
164
167
|
level: "warn",
|
|
165
|
-
text: `There is no terminal here, so this tool cannot ask for the
|
|
168
|
+
text: `There is no terminal here, so this tool cannot ask for the NMTS key. It has to ` +
|
|
166
169
|
`arrive in the environment or in a file named by NMTS_ACCOUNT_CODE_FILE.`,
|
|
167
170
|
});
|
|
168
171
|
}
|
|
@@ -170,13 +173,13 @@ export function adviseFor(env, hasStoredCode) {
|
|
|
170
173
|
out.push({
|
|
171
174
|
level: "note",
|
|
172
175
|
text: `No browser can be opened here. Anything that needs a human check — making an account, ` +
|
|
173
|
-
`
|
|
176
|
+
`renewing the account's four-week check — has to be done on a machine that has one.`,
|
|
174
177
|
});
|
|
175
178
|
}
|
|
176
179
|
if (env.os === "win32") {
|
|
177
180
|
out.push({
|
|
178
181
|
level: "note",
|
|
179
|
-
text: `Windows applies no POSIX file mode, so a stored
|
|
182
|
+
text: `Windows applies no POSIX file mode, so a stored NMTS key inherits the folder's ` +
|
|
180
183
|
`permissions rather than being restricted to one user.`,
|
|
181
184
|
});
|
|
182
185
|
}
|
|
@@ -186,5 +189,22 @@ export function adviseFor(env, hasStoredCode) {
|
|
|
186
189
|
text: `The configuration directory is inside the temporary directory and may be cleared at any time.`,
|
|
187
190
|
});
|
|
188
191
|
}
|
|
192
|
+
// ⛔ THE ONE THING THAT SURPRISES PEOPLE. Three of the five agents this tool knows clear the
|
|
193
|
+
// environment before starting an MCP server and put back a fixed list of names — none of
|
|
194
|
+
// which is ours. So a person who exported the NMTS key, attached the tool, and watched
|
|
195
|
+
// it say "not found" did everything right; the value was dropped between the two. Saying so
|
|
196
|
+
// while the marker is still visible (in the shell, where nothing has been cleared yet) is the
|
|
197
|
+
// only moment it can be said before the failure rather than after it.
|
|
198
|
+
const washing = washingHosts(env.agentHosts);
|
|
199
|
+
if (washing.length > 0) {
|
|
200
|
+
const names = washing.map((id) => HOST_NAMES[id]).join(" and ");
|
|
201
|
+
out.push({
|
|
202
|
+
level: "warn",
|
|
203
|
+
text: `${names} clears the environment before starting an MCP server and restores only a fixed ` +
|
|
204
|
+
`list of names, which does not include NMTS_ACCOUNT_CODE or NMTS_ACCOUNT_CODE_FILE. Those ` +
|
|
205
|
+
`work in a terminal here and will not reach the tool once it is attached. Sign in once ` +
|
|
206
|
+
`with \`${BINARY_NAME} login\` so the NMTS key is in this tool's own file instead.`,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
189
209
|
return out;
|
|
190
210
|
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** A failure this tool understood, with an exit code and something the caller can do. */
|
|
2
|
+
export declare class NmtsError extends Error {
|
|
3
|
+
readonly exitCode: number;
|
|
4
|
+
/** One line naming the next action, or null when there is nothing useful to suggest. */
|
|
5
|
+
readonly nextStep: string | null;
|
|
6
|
+
constructor(message: string, options?: {
|
|
7
|
+
exitCode?: number;
|
|
8
|
+
nextStep?: string | null;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
/** Nothing is signed in on this machine and no code was supplied. */
|
|
12
|
+
export declare class NotLoggedInError extends NmtsError {
|
|
13
|
+
constructor(binary: string, envVar: string);
|
|
14
|
+
}
|
|
15
|
+
/** The command exists but is not built yet. Said plainly rather than failing as if it broke. */
|
|
16
|
+
export declare class NotBuiltYetError extends NmtsError {
|
|
17
|
+
constructor(what: string);
|
|
18
|
+
}
|
|
19
|
+
/** Render a failure for a terminal an agent is reading. */
|
|
20
|
+
export declare function renderError(error: unknown, binary: string): string;
|
|
21
|
+
/** Exit code for an unknown failure, kept distinct from the ones above. */
|
|
22
|
+
export declare const UNKNOWN_FAILURE_EXIT = 1;
|
package/dist/errors.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// tells an agent nothing it can use; "your API key was revoked — issue a new one at
|
|
6
6
|
// nmts.me/account" tells it whether to retry, ask, or stop.
|
|
7
7
|
//
|
|
8
|
-
// ⛔ NOTHING SECRET IS EVER INTERPOLATED. Not the
|
|
8
|
+
// ⛔ NOTHING SECRET IS EVER INTERPOLATED. Not the NMTS key, not the API key, not a session
|
|
9
9
|
// token. An error string is the one place a secret escapes without anybody choosing to print
|
|
10
10
|
// it, and agents copy error strings into logs and prompts by default.
|
|
11
11
|
//
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// unknown error's `message` goes out verbatim, which is right (a swallowed cause is a debug
|
|
14
14
|
// session nobody can start) and is also a hole: an adversarial review made `JSON.parse` fail on
|
|
15
15
|
// the credentials file, and V8's own message quotes about thirty characters of the input —
|
|
16
|
-
// nine symbols of a real
|
|
16
|
+
// nine symbols of a real NMTS key reached stderr. The fix is not here. It is that anything
|
|
17
17
|
// reading a file the code is in must catch its own parser and throw a message of its own
|
|
18
18
|
// (`credentials.ts`, `unusable`). ▶ Any NEW code that parses a secret-bearing file owes the
|
|
19
19
|
// same, and this paragraph is the reason why.
|
|
@@ -32,7 +32,7 @@ export class NmtsError extends Error {
|
|
|
32
32
|
/** Nothing is signed in on this machine and no code was supplied. */
|
|
33
33
|
export class NotLoggedInError extends NmtsError {
|
|
34
34
|
constructor(binary, envVar) {
|
|
35
|
-
super(`No NMTS
|
|
35
|
+
super(`No NMTS key on this machine.`, {
|
|
36
36
|
exitCode: 3,
|
|
37
37
|
nextStep: `Run \`${binary} login\`, or set ${envVar} in the environment.`,
|
|
38
38
|
});
|
package/dist/exit.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** The exit code a failure asks for, or the generic one. */
|
|
2
|
+
export declare function exitCodeFor(error: unknown): number;
|
|
3
|
+
/**
|
|
4
|
+
* Stop a closed pipe from becoming a crash.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ `nmts ls | head` IS AN ORDINARY THING TO DO, and without this it prints a ten-line stack
|
|
7
|
+
* trace instead of the answer. `head` closes the pipe once it has its lines; the next write
|
|
8
|
+
* raises EPIPE, and Node turns an unhandled stream error into a fatal one. Every shell tool is
|
|
9
|
+
* expected to end quietly there — that is what SIGPIPE does for programs that do not intercept
|
|
10
|
+
* it — and an agent piping this into anything would otherwise read a crash and conclude the
|
|
11
|
+
* tool is broken.
|
|
12
|
+
*
|
|
13
|
+
* ⚠ ONLY EPIPE. A write that fails for any other reason is still a real failure and still throws;
|
|
14
|
+
* swallowing all stream errors would hide a full disk behind silence.
|
|
15
|
+
*/
|
|
16
|
+
export declare function endQuietlyOnClosedPipe(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Is the module at `moduleFilename` the program, or is something importing it?
|
|
19
|
+
*
|
|
20
|
+
* ⛔ THE FILENAME IS PASSED IN AND NOT READ HERE. `import.meta.filename` inside this module
|
|
21
|
+
* would name THIS file, which is never the program; the caller's own is the question.
|
|
22
|
+
*
|
|
23
|
+
* ⛔ THE COMPARISON IS BETWEEN REAL PATHS, AND THAT IS THE WHOLE POINT. `npm install -g` does not
|
|
24
|
+
* copy the command onto your PATH — it puts a SYMLINK there, pointing at `dist/main.js` inside
|
|
25
|
+
* the installed package. Node then loads the module by its real
|
|
26
|
+
* path, so the module's own name is `.../dist/main.js` while `process.argv[1]` is
|
|
27
|
+
* `.../bin/nmts`. Comparing those two directly is never true for an installed command: `nmts --version` printed NOTHING and exited 0, and
|
|
28
|
+
* so did every other command. The whole tool did nothing at all, silently, and only when
|
|
29
|
+
* installed — which is the one way a person who is not us runs it.
|
|
30
|
+
*
|
|
31
|
+
* ⚠ `realpathSync` THROWS on a path that is not there. `process.argv[1]` normally exists, but a
|
|
32
|
+
* caller is free to hand Node something else, and a crash before the first line of output would
|
|
33
|
+
* be a worse answer than not running. Not resolving is treated as "not the program", which is
|
|
34
|
+
* the same answer this was giving before symlinks were considered at all.
|
|
35
|
+
*/
|
|
36
|
+
export declare function invokedDirectly(moduleFilename: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Say so if a newer release is already known about, and refresh what is known for next time.
|
|
39
|
+
*
|
|
40
|
+
* ⛔ AFTER THE COMMAND, NOT BEFORE IT. Whatever was asked for has already been printed and the
|
|
41
|
+
* exit code is already decided; nothing here can change either. ⛔ AND ONLY FROM `main`: the
|
|
42
|
+
* tests drive `run` directly, so no test and no embedded caller ever reaches a network.
|
|
43
|
+
*/
|
|
44
|
+
export declare function noteUpdateAfter(argv: readonly string[], version: string): Promise<void>;
|
package/dist/expiry.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { NOTICE_DAYS, NOTICE_EPOCHS, URGENT_DAYS, URGENT_EPOCHS, type DaysLeft } from "./shared/lib/extend/epochs.ts";
|
|
2
|
+
export { NOTICE_DAYS, NOTICE_EPOCHS, URGENT_DAYS, URGENT_EPOCHS };
|
|
3
|
+
export type { DaysLeft };
|
|
4
|
+
/** Where the storage network's clock stands. Built by `epochClock`, never assembled by hand. */
|
|
5
|
+
export interface EpochClock {
|
|
6
|
+
/** The epoch the network is in. */
|
|
7
|
+
readonly current: number;
|
|
8
|
+
/** One epoch's length in milliseconds — read from the network, never assumed. */
|
|
9
|
+
readonly durationMs: number;
|
|
10
|
+
/** When the current epoch began, or null when the network did not say. */
|
|
11
|
+
readonly startedMs: number | null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A clock, or null when what the network answered cannot be counted with.
|
|
15
|
+
*
|
|
16
|
+
* ⛔ THE INVARIANT LIVES IN THE TYPE, so every function below can divide by `durationMs` without
|
|
17
|
+
* asking again. A zero or a NaN out of a chain read would otherwise become an Infinity printed
|
|
18
|
+
* as a number of days, and there would be no single place to have caught it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function epochClock(current: number, durationMs: number, startedMs: number | null): EpochClock | null;
|
|
21
|
+
/**
|
|
22
|
+
* How far ahead a warning stage reaches, in epochs: a floor in days and a floor in epochs, wider
|
|
23
|
+
* one wins.
|
|
24
|
+
*
|
|
25
|
+
* The day count rounds UP, because arriving a tick late is the quiet failure the whole surface
|
|
26
|
+
* exists to prevent.
|
|
27
|
+
*/
|
|
28
|
+
export declare function warningEpochs(clock: EpochClock, days: number, floorEpochs: number): number;
|
|
29
|
+
/**
|
|
30
|
+
* Whole days from `nowMs` until `epoch` is reached, by the network's own clock.
|
|
31
|
+
*
|
|
32
|
+
* With an anchor both ends collapse onto one moment. Without one, the honest answer is the
|
|
33
|
+
* earliest the epoch can arrive — see the header for why the other edge is never used.
|
|
34
|
+
*/
|
|
35
|
+
export declare function daysLeftUntilEpoch(clock: EpochClock, epoch: number, nowMs: number): DaysLeft;
|
|
36
|
+
/**
|
|
37
|
+
* How much trouble one file's storage term is in.
|
|
38
|
+
*
|
|
39
|
+
* `lapsed` is not a prediction: the term ran out and the bytes may already be unreadable. It is
|
|
40
|
+
* kept separate from `urgent` because the action differs — an urgent file can still be extended,
|
|
41
|
+
* a lapsed one usually cannot.
|
|
42
|
+
*/
|
|
43
|
+
export type ExpiryStage = "unrecorded" | "lapsed" | "urgent" | "soon" | "later";
|
|
44
|
+
export declare function stageOf(clock: EpochClock, expiryEpoch: number, nowMs: number): ExpiryStage;
|
|
45
|
+
/**
|
|
46
|
+
* The epoch to ask the server about: everything ending before this is inside the warning window.
|
|
47
|
+
*
|
|
48
|
+
* The cutoff is computed HERE and sent as an absolute number because the server does not read the
|
|
49
|
+
* chain and cannot work it out — `GET /v1/items/expiring` takes the answer, not the question.
|
|
50
|
+
*/
|
|
51
|
+
export declare function warningCutoffEpoch(clock: EpochClock): number;
|
|
52
|
+
/**
|
|
53
|
+
* How long is left, said the way a person reads it.
|
|
54
|
+
*
|
|
55
|
+
* ⚠ "or more" is not hedging: it is the difference between a measurement and a floor, and dropping
|
|
56
|
+
* it would turn a lower bound into a promise about a deletion date.
|
|
57
|
+
*/
|
|
58
|
+
export declare function daysLeftInWords(left: DaysLeft): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ExtendReads } from "./extend-plan.ts";
|
|
2
|
+
export interface Budget {
|
|
3
|
+
/** The address that would sign — the one `nmts wallet` prints and the one somebody funds. */
|
|
4
|
+
readonly address: string;
|
|
5
|
+
/** What the extension costs, in FROST. */
|
|
6
|
+
readonly priceFrost: bigint;
|
|
7
|
+
/** Held now, in base units — or null when the chain could not answer. */
|
|
8
|
+
readonly walFrost: bigint | null;
|
|
9
|
+
readonly suiMist: bigint | null;
|
|
10
|
+
/** The chain fee the dry run measured, in MIST — or null when it could not be measured. */
|
|
11
|
+
readonly feeMist: bigint | null;
|
|
12
|
+
/** Why each unread balance could not be read, in the words the chain gave. */
|
|
13
|
+
readonly unread: readonly string[];
|
|
14
|
+
/** What the wallet is known to be short of, or null when nothing known says it is short. */
|
|
15
|
+
readonly shortfall: string | null;
|
|
16
|
+
}
|
|
17
|
+
/** Read the wallet and measure the fee, then say whether the known numbers cover the purchase. */
|
|
18
|
+
export declare function readBudget(reads: ExtendReads, input: {
|
|
19
|
+
address: string;
|
|
20
|
+
objectIds: readonly string[];
|
|
21
|
+
epochs: number;
|
|
22
|
+
priceFrost: bigint;
|
|
23
|
+
}): Promise<Budget>;
|
|
24
|
+
/** The budget as the machine-readable answer carries it. ⚠ Base units are strings — see `Facts`. */
|
|
25
|
+
export declare function budgetFacts(b: Budget): {
|
|
26
|
+
wallet: string;
|
|
27
|
+
walletWal: string | null;
|
|
28
|
+
walletSui: string | null;
|
|
29
|
+
feeMist: string | null;
|
|
30
|
+
feeSui: string | null;
|
|
31
|
+
};
|
|
32
|
+
/** The next step when the wallet is short: where to send what, said once. */
|
|
33
|
+
export declare function shortfallNextStep(b: Budget): string;
|
|
34
|
+
/** The fee and the wallet, for a person, after the price. */
|
|
35
|
+
export declare function describeBudget(say: (line: string) => void, b: Budget): void;
|