@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
package/dist/commands/push.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// `nmts push <directory>` — a whole directory, with its shape, paid for with credits
|
|
1
|
+
// `nmts push <directory>` — a whole directory, with its shape, paid for with credits — or, with
|
|
2
|
+
// `--pay wallet`, each file's storage bought by the person's own wallet (`put-wallet.ts` does one
|
|
3
|
+
// file at a time: its review, its agreement check and its signatures).
|
|
2
4
|
//
|
|
3
5
|
// ⛔ IT STOPS AT THE FIRST FAILURE, and that is the opposite of what `pull` does. Pulling costs
|
|
4
6
|
// nothing, so carrying on past one bad file saves the nineteen good ones. Pushing SPENDS: a
|
|
@@ -16,14 +18,14 @@
|
|
|
16
18
|
// upload goes to a public storage network. `--hidden` includes them.
|
|
17
19
|
import { readdirSync, statSync } from "node:fs";
|
|
18
20
|
import { basename, join, resolve } from "node:path";
|
|
19
|
-
import { requireConsent } from "../consent.js";
|
|
20
21
|
import { DERIVED, loadCrypto } from "../crypto.js";
|
|
21
22
|
import { normaliseName, normalisePath } from "../drive-paths.js";
|
|
22
23
|
import { NmtsError } from "../errors.js";
|
|
23
24
|
import { addEntry } from "../manifest-write.js";
|
|
24
25
|
import { parseAsked } from "../collision.js";
|
|
26
|
+
import { depositDefaultOf, depositLines, parseDeposit, refuseDepositWithWallet } from "../deposit.js";
|
|
25
27
|
import { setTrashed } from "../item-trash.js";
|
|
26
|
-
import { readFileList } from "../manifest.js";
|
|
28
|
+
import { paddingRuleOf, readFileList } from "../manifest.js";
|
|
27
29
|
import { BINARY_NAME } from "../product.js";
|
|
28
30
|
import { Progress, silentSink, stderrSink } from "../progress.js";
|
|
29
31
|
import { openSession } from "../session.js";
|
|
@@ -33,6 +35,7 @@ import { clearItemRecord, clearReservation } from "../upload-store.js";
|
|
|
33
35
|
import { CREDIT_BYTES, partSizeFor, planAndPrice, UPLOAD_EPOCHS } from "../upload-price.js";
|
|
34
36
|
import { createBlobProtocol, readCurrentEpoch } from "../walrus-write.js";
|
|
35
37
|
import { ensureFolderPath } from "./organise.js";
|
|
38
|
+
import { payerOf, refuseWalletOnlyOptions } from "./put.js";
|
|
36
39
|
export async function push(target, options = {}) {
|
|
37
40
|
const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
|
|
38
41
|
if (target === undefined || target === "") {
|
|
@@ -41,6 +44,19 @@ export async function push(target, options = {}) {
|
|
|
41
44
|
nextStep: `\`${BINARY_NAME} push <directory>\` — a directory on this machine.`,
|
|
42
45
|
});
|
|
43
46
|
}
|
|
47
|
+
const payer = payerOf(options.pay);
|
|
48
|
+
if (payer === "wallet")
|
|
49
|
+
refuseDepositWithWallet(options.deposit);
|
|
50
|
+
if (payer === "credits")
|
|
51
|
+
refuseWalletOnlyOptions(options);
|
|
52
|
+
// ⛔ BEFORE ANYTHING IS WALKED, SEALED OR PAID FOR — a typo must not surface after the money.
|
|
53
|
+
const askedDeposit = parseDeposit(options.deposit);
|
|
54
|
+
if (options.storage !== undefined) {
|
|
55
|
+
throw new NmtsError("--storage is for `put`: a held storage resource holds one blob, and a directory is many.", {
|
|
56
|
+
exitCode: 2,
|
|
57
|
+
nextStep: `Nothing was sent. Leave --storage off to buy new storage for each file.`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
44
60
|
const root = resolve(target);
|
|
45
61
|
let rootStat;
|
|
46
62
|
try {
|
|
@@ -71,7 +87,7 @@ export async function push(target, options = {}) {
|
|
|
71
87
|
// ⛔ READ BEFORE ANYTHING IS SEALED OR PAID FOR — a typo must not surface after the money.
|
|
72
88
|
const asked = parseAsked(options.onCollision);
|
|
73
89
|
const list = await readFileList(session.server, session.apiKey, session.code, session.accountId);
|
|
74
|
-
const rule = list.manifest?.settings
|
|
90
|
+
const rule = paddingRuleOf(list.manifest?.settings);
|
|
75
91
|
// ⛔ WHAT IS ALREADY THERE IS DECIDED BEFORE ANYTHING IS PRICED, so the number printed is what
|
|
76
92
|
// this run will actually spend rather than what a first run would have.
|
|
77
93
|
const entries = list.manifest?.entries ?? [];
|
|
@@ -90,6 +106,14 @@ export async function push(target, options = {}) {
|
|
|
90
106
|
}
|
|
91
107
|
const credits = todo.reduce((sum, one) => sum + planAndPrice(one.size, partSize, rule).credits, 0);
|
|
92
108
|
const bytes = todo.reduce((sum, one) => sum + one.size, 0);
|
|
109
|
+
// Per FILE, not per run: every file in the tree sets the same amount aside.
|
|
110
|
+
const deposit = askedDeposit ?? depositDefaultOf(list.manifest?.settings);
|
|
111
|
+
if (payer === "wallet") {
|
|
112
|
+
// ⛔ THE WALLET PATH PRICES AND AGREES PER FILE, in `put-wallet.ts`: each file is its own review
|
|
113
|
+
// against the chain's quote, and `--dry-run` prints every review and signs nothing.
|
|
114
|
+
const { pushWithWallet } = await import("./push-wallet.js");
|
|
115
|
+
return await pushWithWallet(session, options, { todo, already, found: found.length, bytes, partSize, rule, asked, settings: list.manifest?.settings, folderIds, say });
|
|
116
|
+
}
|
|
93
117
|
if (options.dryRun === true) {
|
|
94
118
|
if (options.json) {
|
|
95
119
|
say(JSON.stringify({
|
|
@@ -98,11 +122,14 @@ export async function push(target, options = {}) {
|
|
|
98
122
|
skipped: already.length,
|
|
99
123
|
bytes,
|
|
100
124
|
credits,
|
|
125
|
+
deposit,
|
|
101
126
|
epochs: UPLOAD_EPOCHS,
|
|
102
127
|
}));
|
|
103
128
|
return 0;
|
|
104
129
|
}
|
|
105
130
|
say(`${todo.length} file${todo.length === 1 ? "" : "s"} ${bytes} bytes → ${credits} credit${credits === 1 ? "" : "s"}`);
|
|
131
|
+
for (const line of depositLines(deposit, todo.length))
|
|
132
|
+
say(line);
|
|
106
133
|
if (already.length > 0) {
|
|
107
134
|
say(` ${already.length} already in the drive, which this would not send again`);
|
|
108
135
|
}
|
|
@@ -110,7 +137,6 @@ export async function push(target, options = {}) {
|
|
|
110
137
|
say(` Nothing was sent and nothing was charged.`);
|
|
111
138
|
return 0;
|
|
112
139
|
}
|
|
113
|
-
requireConsent("spend");
|
|
114
140
|
const progress = new Progress(options.json === true ? silentSink() : stderrSink(), "uploading");
|
|
115
141
|
const crypt = await loadCrypto();
|
|
116
142
|
// ⛔ READ ONCE, AND ONLY IF SOMETHING IS ACTUALLY SENT. It is a chain read: a run whose files are
|
|
@@ -123,6 +149,8 @@ export async function push(target, options = {}) {
|
|
|
123
149
|
};
|
|
124
150
|
if (!options.json) {
|
|
125
151
|
say(`${todo.length} file${todo.length === 1 ? "" : "s"} → ${credits} credit${credits === 1 ? "" : "s"}`);
|
|
152
|
+
for (const line of depositLines(deposit, todo.length))
|
|
153
|
+
say(line);
|
|
126
154
|
if (already.length > 0)
|
|
127
155
|
say(` ${already.length} already there, not sent again`);
|
|
128
156
|
}
|
|
@@ -138,6 +166,7 @@ export async function push(target, options = {}) {
|
|
|
138
166
|
parentId: into,
|
|
139
167
|
partSize,
|
|
140
168
|
rule,
|
|
169
|
+
deposit,
|
|
141
170
|
currentEpoch: await currentEpoch(),
|
|
142
171
|
progress,
|
|
143
172
|
}));
|
|
@@ -161,7 +190,7 @@ export async function push(target, options = {}) {
|
|
|
161
190
|
progress.done();
|
|
162
191
|
}
|
|
163
192
|
if (options.json) {
|
|
164
|
-
say(JSON.stringify({ files: found.length, uploaded: uploaded.length, skipped: already.length, bytes, credits }));
|
|
193
|
+
say(JSON.stringify({ files: found.length, uploaded: uploaded.length, skipped: already.length, bytes, credits, deposit }));
|
|
165
194
|
return 0;
|
|
166
195
|
}
|
|
167
196
|
say(``);
|
|
@@ -189,6 +218,7 @@ async function sendOne(session, crypt, one, ctx) {
|
|
|
189
218
|
currentEpoch: ctx.currentEpoch,
|
|
190
219
|
partSize: ctx.partSize,
|
|
191
220
|
padding: { rule: ctx.rule, unitBytes: CREDIT_BYTES },
|
|
221
|
+
depositCredits: ctx.deposit,
|
|
192
222
|
});
|
|
193
223
|
const now = Date.now();
|
|
194
224
|
// ⛔ FROM THE RESULT, NOT FROM THIS RUN. The key that opens the stored bytes is the key they
|
|
@@ -225,7 +255,7 @@ async function sendOne(session, crypt, one, ctx) {
|
|
|
225
255
|
}
|
|
226
256
|
}
|
|
227
257
|
/** The folder id for a drive path, made if it is not there yet. Remembered for the next file. */
|
|
228
|
-
async function folderFor(session, known, folder) {
|
|
258
|
+
export async function folderFor(session, known, folder) {
|
|
229
259
|
const held = known.get(folder);
|
|
230
260
|
if (held !== undefined)
|
|
231
261
|
return held;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type OnCollision } from "../collision.ts";
|
|
2
|
+
import { type CryptoGlue } from "../crypto.ts";
|
|
3
|
+
import { type Network } from "../network.ts";
|
|
4
|
+
import { Progress } from "../progress.ts";
|
|
5
|
+
import type { PaddingRule } from "../shared/lib/crypto/size-padding.ts";
|
|
6
|
+
import type { AccountSettings } from "../shared/lib/drive/manifest-settings.ts";
|
|
7
|
+
import type { StandingTipInput } from "../standing-tip.ts";
|
|
8
|
+
import { type WalletUploadReads } from "../upload-wallet-plan.ts";
|
|
9
|
+
import type { BlobProtocol, UploadApi } from "../upload-wire.ts";
|
|
10
|
+
import type { SignBlobCertify, SignBlobRegister } from "../wallet-sign.ts";
|
|
11
|
+
import { type PutOptions } from "./put.ts";
|
|
12
|
+
/** What paying from the wallet adds to an upload command. */
|
|
13
|
+
export interface WalletPayOptions {
|
|
14
|
+
/** How many of the storage network's epochs to buy. Default `DEFAULT_UPLOAD_EPOCHS`. */
|
|
15
|
+
epochs?: string | number | undefined;
|
|
16
|
+
/** `fit`, `whole`, or a held resource's object id. Absent = buy new storage. */
|
|
17
|
+
storage?: string | undefined;
|
|
18
|
+
/** The instant the wallet agreement is measured against. */
|
|
19
|
+
now?: number;
|
|
20
|
+
/** ⚠ A SEAM, NOT AN OPTION — no flag reaches it. */
|
|
21
|
+
readChain?: (network: Network, relayUrl: string) => WalletUploadReads | Promise<WalletUploadReads>;
|
|
22
|
+
/** ⛔ SEPARATE FROM THE READS so a test can prove the review stops before this. */
|
|
23
|
+
sign?: {
|
|
24
|
+
register: SignBlobRegister;
|
|
25
|
+
certify: SignBlobCertify;
|
|
26
|
+
};
|
|
27
|
+
/** ⚠ SEAMS, NOT OPTIONS — the standing tip's own read and signature. No flag reaches them. */
|
|
28
|
+
tip?: Pick<StandingTipInput, "readDonation" | "sign">;
|
|
29
|
+
/** The storage-network protocol and the server calls — seams for the tests, as `upload.ts` has. */
|
|
30
|
+
protocol?: (network: Network, bodyBytes: number, onSent: (sent: number, total: number) => void) => BlobProtocol & {
|
|
31
|
+
relayUrl: string;
|
|
32
|
+
};
|
|
33
|
+
api?: UploadApi;
|
|
34
|
+
}
|
|
35
|
+
export type PutWalletOptions = PutOptions & WalletPayOptions;
|
|
36
|
+
/** One local file and where it goes — what `put` and `push` both hand in. */
|
|
37
|
+
export interface WalletUploadFile {
|
|
38
|
+
localPath: string;
|
|
39
|
+
size: number;
|
|
40
|
+
name: string;
|
|
41
|
+
parentId: string | null;
|
|
42
|
+
/** The destination AS TYPED — part of the reservation key. */
|
|
43
|
+
destination: string;
|
|
44
|
+
}
|
|
45
|
+
export interface WalletUploadContext {
|
|
46
|
+
code: string;
|
|
47
|
+
apiKey: string;
|
|
48
|
+
server: string;
|
|
49
|
+
network: Network;
|
|
50
|
+
accountId: string;
|
|
51
|
+
crypt: CryptoGlue;
|
|
52
|
+
partSize: number;
|
|
53
|
+
rule: PaddingRule;
|
|
54
|
+
onCollision?: OnCollision | undefined;
|
|
55
|
+
/** The sealed list's account settings, as read for this run — the standing tip lives in them. */
|
|
56
|
+
settings?: AccountSettings | undefined;
|
|
57
|
+
progress: Progress;
|
|
58
|
+
say: (line: string) => void;
|
|
59
|
+
json: boolean;
|
|
60
|
+
}
|
|
61
|
+
export declare function putWithWallet(target: string | undefined, options: PutWalletOptions): Promise<number>;
|
|
62
|
+
/**
|
|
63
|
+
* Review, agree, upload and record ONE file. Null when `--dry-run` stopped at the review.
|
|
64
|
+
*
|
|
65
|
+
* ⛔ THE SIGNING MODULE IS LOADED AFTER THE AGREEMENT, and only then — a dry run and a refusal
|
|
66
|
+
* never bring the code that can spend into memory.
|
|
67
|
+
*/
|
|
68
|
+
export declare function uploadOneWithWallet(ctx: WalletUploadContext, file: WalletUploadFile, options: WalletPayOptions & {
|
|
69
|
+
dryRun?: boolean | undefined;
|
|
70
|
+
}): Promise<{
|
|
71
|
+
itemId: string;
|
|
72
|
+
savedAs: string;
|
|
73
|
+
replaced: string | null;
|
|
74
|
+
seq: number;
|
|
75
|
+
resumed: boolean;
|
|
76
|
+
facts: Record<string, unknown>;
|
|
77
|
+
} | null>;
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// `nmts put <file> --pay wallet` — one file in, sealed on this machine, its storage bought by the
|
|
2
|
+
// PERSON'S OWN WALLET on the storage network instead of by credits.
|
|
3
|
+
//
|
|
4
|
+
// ⛔ IT PRICES BEFORE IT SIGNS, ALWAYS, in the order `extend` set: the parts are planned, the chain
|
|
5
|
+
// quotes each part in WAL and the relay's tip in SUI, the register transaction is dry-run for
|
|
6
|
+
// its fee, both balances are read — and only then is the review printed, a known shortfall
|
|
7
|
+
// refused, and the `wallet` agreement (scope `storage`) held against the total. `--dry-run`
|
|
8
|
+
// stops at the review and never loads the signing module.
|
|
9
|
+
//
|
|
10
|
+
// ⛔ THE SERVER IS TOLD THE STORAGE IS THE PERSON'S BY SAYING NOTHING ELSE. `POST /v1/items` forces
|
|
11
|
+
// treasury ownership only on a part that names a certified reservation; a wallet-paid part names
|
|
12
|
+
// none and carries the blob object and the end epoch instead, exactly as the browser's own
|
|
13
|
+
// wallet-paid commit does (`upload-steps.ts`).
|
|
14
|
+
//
|
|
15
|
+
// ⛔ A HELD RESOURCE IS OFFERED, NEVER CHOSEN FOR THE PERSON — the owner's rule for storage
|
|
16
|
+
// resources: the leftover is shown in bytes and the person decides. The review names how many
|
|
17
|
+
// free resources the wallet holds; `--storage fit|whole|<id>` uses one and the review says in
|
|
18
|
+
// bytes what is cut free or bound with the file.
|
|
19
|
+
import { basename, resolve } from "node:path";
|
|
20
|
+
import { identityOf } from "../account.js";
|
|
21
|
+
import { requireAccountCode } from "../code-access.js";
|
|
22
|
+
import { parseAsked } from "../collision.js";
|
|
23
|
+
import { DERIVED, loadCrypto } from "../crypto.js";
|
|
24
|
+
import { API_KEY_ENV_VAR, readCredentialsFile, resolveApiKey } from "../credentials.js";
|
|
25
|
+
import { NmtsError } from "../errors.js";
|
|
26
|
+
import { setTrashed } from "../item-trash.js";
|
|
27
|
+
import { addEntry } from "../manifest-write.js";
|
|
28
|
+
import { paddingRuleOf, readFileList } from "../manifest.js";
|
|
29
|
+
import { resolveNetwork } from "../network.js";
|
|
30
|
+
import { BINARY_NAME } from "../product.js";
|
|
31
|
+
import { Progress, silentSink, stderrSink } from "../progress.js";
|
|
32
|
+
import { sealedLenFor } from "../seal.js";
|
|
33
|
+
import { resolveServer } from "../server.js";
|
|
34
|
+
import { statusOf } from "../shared/lib/storage-control/plan.js";
|
|
35
|
+
import { createUploadApi } from "../upload-api.js";
|
|
36
|
+
import { fileSource, partKeysOf, uploadFile } from "../upload-file.js";
|
|
37
|
+
import { CREDIT_BYTES, measureLocal, partSizeFor, planAndPrice } from "../upload-price.js";
|
|
38
|
+
import { clearItemRecord, clearReservation } from "../upload-store.js";
|
|
39
|
+
import { walletRail } from "../upload-wallet.js";
|
|
40
|
+
import { chooseUploadEpochs, daysOf, describeUploadReview, parseStorageAsk, pickResource, uploadBudget, uploadShortfallNextStep, walPrice, } from "../upload-wallet-plan.js";
|
|
41
|
+
import { coinAmount, walletAddress } from "../wallet.js";
|
|
42
|
+
import { recordWalletSpend, requireWalletGrant } from "../wallet-grant.js";
|
|
43
|
+
import { createBlobProtocol } from "../walrus-write.js";
|
|
44
|
+
import { folderIdFor } from "./put.js";
|
|
45
|
+
export async function putWithWallet(target, options) {
|
|
46
|
+
const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
|
|
47
|
+
if (target === undefined || target === "") {
|
|
48
|
+
throw new NmtsError("Say which file to put.", { exitCode: 2, nextStep: `\`${BINARY_NAME} put <file> --pay wallet\`` });
|
|
49
|
+
}
|
|
50
|
+
const resolved = await requireAccountCode();
|
|
51
|
+
const key = resolveApiKey();
|
|
52
|
+
if (key === null) {
|
|
53
|
+
throw new NmtsError("This account has no API key on this machine, and the server needs one.", {
|
|
54
|
+
exitCode: 3,
|
|
55
|
+
nextStep: `Make a key on the account screen at nmts.me and put it in ${API_KEY_ENV_VAR}, or store it with \`${BINARY_NAME} login\`.`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const localPath = resolve(target);
|
|
59
|
+
const size = measureLocal(localPath);
|
|
60
|
+
const stored = readCredentialsFile();
|
|
61
|
+
const server = resolveServer(options.server ?? stored?.server);
|
|
62
|
+
const network = resolveNetwork(server, options.network ?? stored?.network);
|
|
63
|
+
const identity = await identityOf(resolved.code);
|
|
64
|
+
const crypt = await loadCrypto();
|
|
65
|
+
const asked = parseAsked(options.onCollision);
|
|
66
|
+
const list = await readFileList(server, key.key, resolved.code, identity.accountId);
|
|
67
|
+
const rule = paddingRuleOf(list.manifest?.settings);
|
|
68
|
+
const name = options.name ?? basename(localPath);
|
|
69
|
+
const destination = (options.to ?? "").replace(/^\.?\//, "").replace(/\/$/, "");
|
|
70
|
+
const parentId = folderIdFor(options.to, list.manifest?.entries ?? []);
|
|
71
|
+
const ctx = {
|
|
72
|
+
code: resolved.code,
|
|
73
|
+
apiKey: key.key,
|
|
74
|
+
server,
|
|
75
|
+
network,
|
|
76
|
+
accountId: identity.accountId,
|
|
77
|
+
crypt,
|
|
78
|
+
partSize: partSizeFor(options.partSize),
|
|
79
|
+
rule,
|
|
80
|
+
onCollision: asked,
|
|
81
|
+
settings: list.manifest?.settings,
|
|
82
|
+
progress: new Progress(options.json === true ? silentSink() : stderrSink(), "uploading"),
|
|
83
|
+
say,
|
|
84
|
+
json: options.json === true,
|
|
85
|
+
};
|
|
86
|
+
const done = await uploadOneWithWallet(ctx, { localPath, size, name, parentId, destination }, options);
|
|
87
|
+
if (done === null)
|
|
88
|
+
return 0;
|
|
89
|
+
if (options.json) {
|
|
90
|
+
say(JSON.stringify({ id: done.itemId, name: done.savedAs, ...done.facts, resumed: done.resumed, renamed: done.savedAs !== name, ...(done.replaced ? { replacedIntoTrash: done.replaced } : {}), fileListVersion: done.seq }));
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
say(` saved as ${done.savedAs}`);
|
|
94
|
+
if (done.replaced)
|
|
95
|
+
say(` A file called ${name} was already there. It is in the trash now — ${BINARY_NAME} restore brings it back for 30 days.`);
|
|
96
|
+
else if (done.savedAs !== name)
|
|
97
|
+
say(` A file called ${name} was already there, so this one was numbered rather than replacing it.`);
|
|
98
|
+
if (done.resumed)
|
|
99
|
+
say(` This finished an upload a previous run had already signed for. Nothing was signed now.`);
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Review, agree, upload and record ONE file. Null when `--dry-run` stopped at the review.
|
|
104
|
+
*
|
|
105
|
+
* ⛔ THE SIGNING MODULE IS LOADED AFTER THE AGREEMENT, and only then — a dry run and a refusal
|
|
106
|
+
* never bring the code that can spend into memory.
|
|
107
|
+
*/
|
|
108
|
+
export async function uploadOneWithWallet(ctx, file, options) {
|
|
109
|
+
const { say } = ctx;
|
|
110
|
+
const { plan, sealedBytes, sealFor } = planAndPrice(file.size, ctx.partSize, ctx.rule);
|
|
111
|
+
const sealedLens = plan.map((range) => sealedLenFor(sealFor(range)));
|
|
112
|
+
const storageAsk = parseStorageAsk(options.storage);
|
|
113
|
+
if (storageAsk !== null && plan.length > 1) {
|
|
114
|
+
throw new NmtsError(`A held storage resource holds one blob, and this file is ${plan.length} parts.`, {
|
|
115
|
+
exitCode: 4,
|
|
116
|
+
nextStep: `Nothing was signed. Leave --storage off, or raise --part-size so the file is one part.`,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const protocol = (options.protocol ?? createBlobProtocol)(ctx.network, sealedBytes, (sent, total) => ctx.progress.update(sent, total));
|
|
120
|
+
const reads = await (options.readChain ?? defaultReads)(ctx.network, protocol.relayUrl);
|
|
121
|
+
const window = await reads.readWindow();
|
|
122
|
+
if (window === null) {
|
|
123
|
+
throw new NmtsError(`The ${ctx.network} storage network could not be read.`, {
|
|
124
|
+
exitCode: 1,
|
|
125
|
+
nextStep: `Nothing was signed and nothing was sent. Which epoch the network is in, and how far ahead it will sell, are facts only the chain has — this tool will not spend against a guess.`,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const epochs = chooseUploadEpochs(options.epochs, window);
|
|
129
|
+
const endEpoch = window.clock.current + epochs;
|
|
130
|
+
const quotes = await reads.quoteParts(sealedLens, epochs);
|
|
131
|
+
const address = await walletAddress(ctx.code);
|
|
132
|
+
let storage = { kind: "buy" };
|
|
133
|
+
let heldResources = null;
|
|
134
|
+
if (storageAsk !== null) {
|
|
135
|
+
const firstQuote = quotes[0];
|
|
136
|
+
const firstLen = sealedLens[0];
|
|
137
|
+
if (firstQuote === undefined || firstLen === undefined)
|
|
138
|
+
throw new NmtsError("unreachable: a plan with no part");
|
|
139
|
+
let resources;
|
|
140
|
+
try {
|
|
141
|
+
resources = await reads.readStorage(address);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
throw new NmtsError("The storage resources this wallet holds could not be read.", {
|
|
145
|
+
exitCode: 1,
|
|
146
|
+
nextStep: `Nothing was signed. That is not the same as holding none. Cause: ${error instanceof Error ? error.message : String(error)}`,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const encoded = await reads.encodedLength(address, firstLen);
|
|
150
|
+
storage = pickResource(storageAsk, resources, encoded, window.clock.current, endEpoch, firstQuote.writeFrost);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
heldResources = await reads.readStorage(address).then((rows) => rows.filter((r) => statusOf(r, window.clock.current) === "usable").length, () => null);
|
|
154
|
+
}
|
|
155
|
+
const [purse, feeMist] = await Promise.all([
|
|
156
|
+
reads.readWallet(address),
|
|
157
|
+
reads.estimateRegisterGas({
|
|
158
|
+
sender: address,
|
|
159
|
+
sealedLen: Math.max(...sealedLens),
|
|
160
|
+
epochs,
|
|
161
|
+
storage: storage.kind === "buy" ? storage : { kind: "reuse", objectId: storage.objectId, cutToBytes: storage.cutToBytes, writeFrost: storage.writeFrost },
|
|
162
|
+
}),
|
|
163
|
+
]);
|
|
164
|
+
const budget = uploadBudget({ address, purse, feeMist, quotes, storage });
|
|
165
|
+
const tipMist = quotes.reduce((sum, q) => sum + q.tipMist, 0n);
|
|
166
|
+
const facts = {
|
|
167
|
+
bytes: file.size,
|
|
168
|
+
sealedBytes,
|
|
169
|
+
parts: plan.length,
|
|
170
|
+
epochs,
|
|
171
|
+
days: daysOf(window, epochs),
|
|
172
|
+
endEpoch,
|
|
173
|
+
paidFrom: "wallet",
|
|
174
|
+
priceFrost: budget.walNeededFrost.toString(),
|
|
175
|
+
priceWal: coinAmount(budget.walNeededFrost),
|
|
176
|
+
tipMist: tipMist.toString(),
|
|
177
|
+
tipSui: coinAmount(tipMist),
|
|
178
|
+
feeMist: feeMist === null ? null : feeMist.toString(),
|
|
179
|
+
feeSui: feeMist === null ? null : coinAmount(feeMist),
|
|
180
|
+
wallet: address,
|
|
181
|
+
walletWal: budget.walFrost === null ? null : coinAmount(budget.walFrost),
|
|
182
|
+
walletSui: budget.suiMist === null ? null : coinAmount(budget.suiMist),
|
|
183
|
+
storage: storage.kind === "buy"
|
|
184
|
+
? { kind: "buy" }
|
|
185
|
+
: { kind: "reuse", objectId: storage.objectId, cutToBytes: storage.cutToBytes, leftoverBytes: storage.leftoverBytes },
|
|
186
|
+
};
|
|
187
|
+
if (!ctx.json) {
|
|
188
|
+
describeUploadReview(say, { name: file.name, bytes: file.size, parts: plan.length, epochs, days: daysOf(window, epochs), endEpoch, walNeeded: walPrice(quotes, storage), tipMist, storage, heldResources }, budget);
|
|
189
|
+
}
|
|
190
|
+
if (options.dryRun === true) {
|
|
191
|
+
if (ctx.json)
|
|
192
|
+
say(JSON.stringify({ dryRun: true, name: file.name, ...facts, signed: false }));
|
|
193
|
+
else {
|
|
194
|
+
say(``);
|
|
195
|
+
if (budget.shortfall !== null)
|
|
196
|
+
say(` ⚠ ${budget.shortfall} As it stands, it would be refused.`);
|
|
197
|
+
say(` Nothing was signed and nothing was sent. Run the same command without --dry-run to upload it.`);
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
// ⛔ A WALLET KNOWN TO BE SHORT IS REFUSED BEFORE THE AGREEMENT IS ASKED FOR (`extend-budget.ts`).
|
|
202
|
+
if (budget.shortfall !== null) {
|
|
203
|
+
throw new NmtsError(budget.shortfall, { exitCode: 4, nextStep: uploadShortfallNextStep(budget) });
|
|
204
|
+
}
|
|
205
|
+
// ⛔ THE ONE GATE BETWEEN A PROGRAM AND SOMEBODY'S WALLET. Everything above is a read.
|
|
206
|
+
requireWalletGrant("seal", { walFrost: budget.walNeededFrost, suiMist: budget.suiNeededMist }, new Date(options.now ?? Date.now()));
|
|
207
|
+
const sign = options.sign ?? (await signers());
|
|
208
|
+
const derived = ctx.crypt.kdf_derive(ctx.crypt.account_code_parse(ctx.code));
|
|
209
|
+
const dataKey = derived.slice(DERIVED.dataKey[0], DERIVED.dataKey[1]);
|
|
210
|
+
derived.fill(0);
|
|
211
|
+
let result;
|
|
212
|
+
try {
|
|
213
|
+
result = await uploadFile({
|
|
214
|
+
api: options.api ?? createUploadApi(ctx.server, ctx.apiKey),
|
|
215
|
+
protocol,
|
|
216
|
+
crypt: ctx.crypt,
|
|
217
|
+
dataKey,
|
|
218
|
+
source: fileSource(file.localPath, file.size),
|
|
219
|
+
name: file.name,
|
|
220
|
+
parentId: file.parentId,
|
|
221
|
+
destination: file.destination,
|
|
222
|
+
relayUrl: protocol.relayUrl,
|
|
223
|
+
epochs,
|
|
224
|
+
currentEpoch: window.clock.current,
|
|
225
|
+
partSize: ctx.partSize,
|
|
226
|
+
padding: { rule: ctx.rule, unitBytes: CREDIT_BYTES },
|
|
227
|
+
onStep: (step) => tell(ctx, file.size, step),
|
|
228
|
+
buy: walletRail({
|
|
229
|
+
network: ctx.network,
|
|
230
|
+
code: ctx.code,
|
|
231
|
+
relayUrl: protocol.relayUrl,
|
|
232
|
+
epochs,
|
|
233
|
+
storage,
|
|
234
|
+
quotes,
|
|
235
|
+
feeMist,
|
|
236
|
+
signRegister: sign.register,
|
|
237
|
+
signCertify: sign.certify,
|
|
238
|
+
onSpend: recordWalletSpend,
|
|
239
|
+
}),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
ctx.progress.done();
|
|
244
|
+
dataKey.fill(0);
|
|
245
|
+
}
|
|
246
|
+
const now = Date.now();
|
|
247
|
+
const added = await addEntry({
|
|
248
|
+
server: ctx.server,
|
|
249
|
+
apiKey: ctx.apiKey,
|
|
250
|
+
code: ctx.code,
|
|
251
|
+
accountId: ctx.accountId,
|
|
252
|
+
...(ctx.onCollision !== undefined ? { onCollision: ctx.onCollision } : {}),
|
|
253
|
+
entry: {
|
|
254
|
+
id: result.itemId,
|
|
255
|
+
parentId: file.parentId,
|
|
256
|
+
kind: 1,
|
|
257
|
+
name: result.entry.name,
|
|
258
|
+
size: result.entry.plaintextLen,
|
|
259
|
+
createdAt: now,
|
|
260
|
+
updatedAt: now,
|
|
261
|
+
dekWrapped: result.entry.dekWrapped,
|
|
262
|
+
contentHashCt: result.entry.contentHashCt,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
// ⛔ ONLY NOW, AND EVERY PART — the same order `put.ts` keeps and for the same reason.
|
|
266
|
+
clearItemRecord(result.fileKey);
|
|
267
|
+
for (const record of partKeysOf(result.fileKey, result.parts))
|
|
268
|
+
clearReservation(record);
|
|
269
|
+
if (added.replaced)
|
|
270
|
+
await setTrashed(ctx.server, ctx.apiKey, added.replaced.id, true);
|
|
271
|
+
// ⛔ THE STANDING SHARE, AFTER THE PAYMENT AND NEVER INSIDE IT: a gift that fails must not fail
|
|
272
|
+
// the upload. A resumed run paid nothing now, so it owes nothing now. In --json the tip speaks
|
|
273
|
+
// on stderr — stdout carries the machine's one answer and nothing else.
|
|
274
|
+
await (await import("../standing-tip.js")).standingTipAfter({
|
|
275
|
+
server: ctx.server,
|
|
276
|
+
network: ctx.network,
|
|
277
|
+
code: ctx.code,
|
|
278
|
+
settings: ctx.settings,
|
|
279
|
+
paidWalFrost: result.resumed ? 0n : budget.walNeededFrost,
|
|
280
|
+
say: ctx.json ? (line) => void process.stderr.write(`${line}\n`) : say,
|
|
281
|
+
...(options.tip ?? {}),
|
|
282
|
+
});
|
|
283
|
+
return { itemId: result.itemId, savedAs: added.name, replaced: added.replaced?.id ?? null, seq: added.seq, resumed: result.resumed, facts };
|
|
284
|
+
}
|
|
285
|
+
/** Each step, for a person watching. The signatures are named as what they are. */
|
|
286
|
+
function tell(ctx, size, step) {
|
|
287
|
+
if (ctx.json || step.step === "planning")
|
|
288
|
+
return;
|
|
289
|
+
const { say } = ctx;
|
|
290
|
+
if (step.step === "hashing")
|
|
291
|
+
return say(` reading ${size} bytes`);
|
|
292
|
+
const where = step.parts > 1 ? ` [${step.partIndex + 1}/${step.parts}]` : "";
|
|
293
|
+
if (step.step === "sealing")
|
|
294
|
+
say(` sealing${where} ${step.bytes} bytes`);
|
|
295
|
+
if (step.step === "resuming")
|
|
296
|
+
say(` picking up a part already ${step.state}${where}`);
|
|
297
|
+
if (step.step === "encoding")
|
|
298
|
+
say(` preparing${where} ${step.bytes} sealed bytes`);
|
|
299
|
+
if (step.step === "reserving")
|
|
300
|
+
say(` signing the registration${where} — WAL and the relay tip leave the wallet`);
|
|
301
|
+
if (step.step === "uploading")
|
|
302
|
+
say(` uploading${where} ${step.bytes} bytes to ${step.relayUrl}`);
|
|
303
|
+
if (step.step === "certifying") {
|
|
304
|
+
ctx.progress.done();
|
|
305
|
+
say(` signing the certification${where} — gas only`);
|
|
306
|
+
}
|
|
307
|
+
if (step.step === "committing")
|
|
308
|
+
say(` saving to the drive`);
|
|
309
|
+
}
|
|
310
|
+
async function defaultReads(network, relayUrl) {
|
|
311
|
+
return (await import("../upload-wallet-chain.js")).walletUploadReads(network, relayUrl);
|
|
312
|
+
}
|
|
313
|
+
async function signers() {
|
|
314
|
+
const signing = await import("../wallet-sign.js");
|
|
315
|
+
return { register: signing.signBlobRegister, certify: signing.signBlobCertify };
|
|
316
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ManifestEntry } from "../shared/lib/drive/manifest-codec.ts";
|
|
2
|
+
export interface PutOptions {
|
|
3
|
+
server?: string | undefined;
|
|
4
|
+
network?: string | undefined;
|
|
5
|
+
/** The name it gets in the drive. Defaults to the local file's own name. */
|
|
6
|
+
name?: string | undefined;
|
|
7
|
+
/** Destination folder, as `nmts ls` prints it. The drive root when absent. */
|
|
8
|
+
to?: string | undefined;
|
|
9
|
+
/** Say what it would cost and stop. Nothing is sealed, sent, or charged. */
|
|
10
|
+
dryRun?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* How much of the file goes into one part, in bytes. Defaults to `DEFAULT_PART_BYTES`.
|
|
13
|
+
*
|
|
14
|
+
* Bigger parts mean fewer reservations, and every reservation counts against the account's
|
|
15
|
+
* daily spending allowance; smaller parts mean less memory and a shorter piece of work to lose
|
|
16
|
+
* when something goes wrong. An exact number, not a menu — a person who knows what their machine
|
|
17
|
+
* can hold should be able to say so.
|
|
18
|
+
*/
|
|
19
|
+
partSize?: string | number | undefined;
|
|
20
|
+
/** What THIS run does about a name already in use. Absent = this machine's setting. */
|
|
21
|
+
onCollision?: string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* How many credits THIS upload sets aside as a deposit on the file, 0 to 64. Absent = the
|
|
24
|
+
* account's own default (`nmts deposit`), which is 64 until somebody sets another.
|
|
25
|
+
*/
|
|
26
|
+
deposit?: string | number | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Who pays for the storage: `credits` (absent) or `wallet`.
|
|
29
|
+
*
|
|
30
|
+
* ⛔ A VALUE, NOT A FLAG, so that a command line says which money it spends. Everything about the
|
|
31
|
+
* wallet path — the epochs, the review, the agreement, the signatures — is in `put-wallet.ts`.
|
|
32
|
+
*/
|
|
33
|
+
pay?: string | undefined;
|
|
34
|
+
/** `--pay wallet`: how many epochs to buy. Refused with credits, whose term is fixed. */
|
|
35
|
+
epochs?: string | number | undefined;
|
|
36
|
+
/** `--pay wallet`: a held storage resource to use — `fit`, `whole`, or its object id. */
|
|
37
|
+
storage?: string | undefined;
|
|
38
|
+
json?: boolean;
|
|
39
|
+
write?: (line: string) => void;
|
|
40
|
+
}
|
|
41
|
+
/** Who pays, or a refusal for a payer this tool does not know. */
|
|
42
|
+
export declare function payerOf(pay: string | undefined): "credits" | "wallet";
|
|
43
|
+
/** The two options that only mean something when the wallet pays, refused when it does not. */
|
|
44
|
+
export declare function refuseWalletOnlyOptions(options: {
|
|
45
|
+
epochs?: string | number | undefined;
|
|
46
|
+
storage?: string | undefined;
|
|
47
|
+
}): void;
|
|
48
|
+
/**
|
|
49
|
+
* The folder id `--to` names, or null for the root. Refuses rather than guessing.
|
|
50
|
+
*
|
|
51
|
+
* ⛔ IT IS THE SAME LOOKUP EVERY OTHER COMMAND USES. This had its own walk and its own
|
|
52
|
+
* `e.deletedAt` test, which meant it would happily accept a folder whose PARENT was in the
|
|
53
|
+
* trash and put a paid-for upload somewhere the drive does not show (2026-08-23).
|
|
54
|
+
*/
|
|
55
|
+
export declare function folderIdFor(wanted: string | undefined, entries: readonly ManifestEntry[]): string | null;
|
|
56
|
+
export declare function put(target: string | undefined, options?: PutOptions): Promise<number>;
|