@needmoretruth/nmts-cli 0.39.0 → 0.40.0
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/CHANGELOG.md +7 -0
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/dist/artifact-about.d.ts +1 -1
- package/dist/commands/extend.d.ts +3 -3
- package/dist/commands/extend.js +52 -161
- package/dist/commands/wallet-storage-ops.d.ts +2 -2
- package/dist/commands/wallet-storage-ops.js +108 -134
- package/dist/commands/wallet-storage.d.ts +3 -8
- package/dist/commands/wallet-storage.js +16 -37
- package/dist/product.d.ts +1 -1
- package/dist/product.js +1 -1
- package/dist/storage-control/extend.d.ts +124 -0
- package/dist/storage-control/extend.js +189 -0
- package/dist/storage-control/hints.d.ts +20 -0
- package/dist/storage-control/hints.js +11 -0
- package/dist/storage-control/list.d.ts +55 -0
- package/dist/storage-control/list.js +71 -0
- package/dist/storage-control/reshape.d.ts +120 -0
- package/dist/storage-control/reshape.js +188 -0
- package/dist/storage-control-chain.d.ts +1 -1
- package/dist/storage-control.d.ts +13 -0
- package/dist/storage-control.js +24 -0
- package/dist/wallet-storage-chain.d.ts +1 -1
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
Each version's entry is what changed for the person or the program using `nmts`. The product's own
|
|
4
4
|
update history, which covers the site and the server too, is at https://nmts.me/updates.
|
|
5
5
|
|
|
6
|
+
## 0.40.0 — 2026-09-20
|
|
7
|
+
|
|
8
|
+
- The library surface has a new entry, `@needmoretruth/nmts-cli/storage-control`: what `nmts extend`
|
|
9
|
+
and `nmts wallet storage` (the listing, `split`, `merge`, `transfer`) do, without the prompts or the
|
|
10
|
+
printing. The commands are unchanged: the same reviews, the same `--yes`, the same unlock, the same
|
|
11
|
+
exit codes. `nmts-sdk` 0.6.0 is built on it.
|
|
12
|
+
|
|
6
13
|
## 0.39.0 — 2026-09-20
|
|
7
14
|
|
|
8
15
|
- The library surface has a new entry, `@needmoretruth/nmts-cli/drive-erase`: what `nmts erase` does,
|
package/README.ko.md
CHANGED
|
@@ -50,7 +50,7 @@ nmts --help
|
|
|
50
50
|
|
|
51
51
|
```sh
|
|
52
52
|
npm install -g github:needmoretruth/nmts-cli # 기본 브랜치
|
|
53
|
-
npm install -g github:needmoretruth/nmts-cli#v0.
|
|
53
|
+
npm install -g github:needmoretruth/nmts-cli#v0.40.0 # 버전을 고정할 때
|
|
54
54
|
npm install -g https://github.com/needmoretruth/nmts-cli/releases/latest/download/nmts.tgz
|
|
55
55
|
```
|
|
56
56
|
|
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ default branch, from a pinned version, or from the tarball attached to the
|
|
|
51
51
|
|
|
52
52
|
```sh
|
|
53
53
|
npm install -g github:needmoretruth/nmts-cli # the default branch
|
|
54
|
-
npm install -g github:needmoretruth/nmts-cli#v0.
|
|
54
|
+
npm install -g github:needmoretruth/nmts-cli#v0.40.0 # a pinned version
|
|
55
55
|
npm install -g https://github.com/needmoretruth/nmts-cli/releases/latest/download/nmts.tgz
|
|
56
56
|
```
|
|
57
57
|
|
package/dist/artifact-about.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare const CRYPTO_SPEC_URL = "https://github.com/needmoretruth/nmts-re
|
|
|
11
11
|
* is the field's own contract — what the writer says about itself — and a person holding two
|
|
12
12
|
* copies of one account's artefacts can then tell which program made each.
|
|
13
13
|
*/
|
|
14
|
-
export declare const WRITTEN_BY = "nmts-cli 0.
|
|
14
|
+
export declare const WRITTEN_BY = "nmts-cli 0.40.0";
|
|
15
15
|
/** Which of the three artefacts a wrapper is. A reader holding several can sort them. */
|
|
16
16
|
export type ArtifactKind = "recovery-list" | "file-list" | "recovery-kit";
|
|
17
17
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ExtendReads, SignExtension } from "../extend-plan.ts";
|
|
2
2
|
import type { StandingTipInput } from "../standing-tip.ts";
|
|
3
3
|
export interface ExtendOptions {
|
|
4
4
|
server?: string | undefined;
|
|
@@ -14,8 +14,8 @@ export interface ExtendOptions {
|
|
|
14
14
|
/**
|
|
15
15
|
* The chain reads.
|
|
16
16
|
*
|
|
17
|
-
* ⚠ A SEAM, NOT AN OPTION — no flag reaches it.
|
|
18
|
-
*
|
|
17
|
+
* ⚠ A SEAM, NOT AN OPTION — no flag reaches it. A test that talked to a live storage network
|
|
18
|
+
* could not run offline and could never be asked to be at its own ceiling.
|
|
19
19
|
*/
|
|
20
20
|
readChain?: (network: string) => Promise<ExtendReads> | ExtendReads;
|
|
21
21
|
/**
|
package/dist/commands/extend.js
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
// nobody — NMTS included — can reverse it. That difference is said out loud, in the output,
|
|
7
7
|
// before the agreement is asked for.
|
|
8
8
|
//
|
|
9
|
-
// ⛔ IT PRICES BEFORE IT SPENDS, ALWAYS. The reads and the quote are free and happen first
|
|
10
|
-
// `--dry-run` answers with a real number and never reaches the key.
|
|
11
|
-
// run without `requireWalletGrant("extend", …)` having passed.
|
|
9
|
+
// ⛔ IT PRICES BEFORE IT SPENDS, ALWAYS. The reads and the quote are free and happen first
|
|
10
|
+
// (`planExtension`), so `--dry-run` answers with a real number and never reaches the key.
|
|
11
|
+
// Nothing below the quote can run without `requireWalletGrant("extend", …)` having passed.
|
|
12
12
|
//
|
|
13
13
|
// ⛔ THE SERVER DOES NOT EXTEND ANYTHING, and this command is shaped by that. `POST
|
|
14
14
|
// /v1/items/{id}/extended` means "record an extension the device already signed": the storage is
|
|
@@ -19,21 +19,19 @@
|
|
|
19
19
|
// ⛔ AND A FILE THAT IS NOT RUNNING OUT IS NOT EXTENDED BY ACCIDENT. Extending early loses nothing
|
|
20
20
|
// — epochs are added to what is left — so this is not a refusal on principle; it is a refusal
|
|
21
21
|
// to spend money on a deadline nobody is near, unless somebody says so with `--yes`.
|
|
22
|
+
//
|
|
23
|
+
// ⚠ WHAT IS DECIDED HERE AND WHAT IS NOT. The reads, the arithmetic, the price, the balances, the
|
|
24
|
+
// signature and the recording are `storage-control.ts`, which the SDK calls as well; this file is
|
|
25
|
+
// the terminal over them — the sentences, the agreement, the standing gift and the exit code.
|
|
22
26
|
import { recordWalletSpend, requireWalletGrant } from "../wallet-grant.js";
|
|
23
|
-
import { request } from "../api.js";
|
|
24
|
-
import { buildIndex, entryAt, fullPathOf, KIND_FILE, normalisePath } from "../drive-paths.js";
|
|
25
27
|
import { NmtsError } from "../errors.js";
|
|
26
|
-
import { daysLeftInWords
|
|
27
|
-
import {
|
|
28
|
-
import { isRecord } from "../guards.js";
|
|
29
|
-
import { readFileList } from "../manifest.js";
|
|
28
|
+
import { daysLeftInWords } from "../expiry.js";
|
|
29
|
+
import { describeBudget, shortfallNextStep } from "../extend-budget.js";
|
|
30
30
|
import { resolveNetwork } from "../network.js";
|
|
31
31
|
import { BINARY_NAME } from "../product.js";
|
|
32
32
|
import { openSession } from "../session.js";
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import { activeWalletOf } from "../shared/lib/drive/manifest-settings.js";
|
|
36
|
-
import { budgetFacts, describeBudget, readBudget, shortfallNextStep } from "../extend-budget.js";
|
|
33
|
+
import { applyExtension, planExtension } from "../storage-control.js";
|
|
34
|
+
import { walletIndexOf } from "../wallet-pay-index.js";
|
|
37
35
|
export async function extend(target, options = {}) {
|
|
38
36
|
const say = options.write ?? ((line) => process.stdout.write(`${line}\n`));
|
|
39
37
|
const now = options.now ?? Date.now();
|
|
@@ -44,129 +42,50 @@ export async function extend(target, options = {}) {
|
|
|
44
42
|
});
|
|
45
43
|
}
|
|
46
44
|
const session = await openSession(options);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
exitCode: 4,
|
|
60
|
-
nextStep: "That is a folder. Nothing was signed and nothing was charged — storage is bought per " +
|
|
61
|
-
"file, so this takes one file at a time.",
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
const path = fullPathOf(buildIndex(entries), entry);
|
|
65
|
-
// ⛔ THE SERVER SAYS WHICH BLOBS, AND NOTHING ELSE. Its `expiry_epoch` is client-reported and
|
|
66
|
-
// advisory; a command that spends money reads the chain's own answer below.
|
|
67
|
-
const preview = asExtendPreview(await request(session.server, `/v1/items/${encodeURIComponent(entry.id)}/extend-preview`, {
|
|
68
|
-
token: session.apiKey,
|
|
69
|
-
}));
|
|
70
|
-
if (preview.targets.length === 0) {
|
|
71
|
-
throw new NmtsError(`Nothing on "${path}" can be extended from here.`, {
|
|
72
|
-
exitCode: 4,
|
|
73
|
-
nextStep: nothingToExtend(preview),
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
const reads = await (options.readChain ?? defaultReads)(session.network);
|
|
77
|
-
const window = await reads.readWindow();
|
|
78
|
-
if (window === null) {
|
|
79
|
-
// ⛔ Not "nothing needs extending". The two look identical from outside and mean opposite things.
|
|
80
|
-
throw new NmtsError(`The ${session.network} storage network could not be read.`, {
|
|
81
|
-
exitCode: 1,
|
|
82
|
-
nextStep: `Nothing was signed and nothing was charged. Which epoch the network is in, and how far ` +
|
|
83
|
-
`ahead it will sell, are facts only the chain has — this tool will not spend against a ` +
|
|
84
|
-
`guess. Try again, or name a different Sui node in NMTS_SUI_RPC.`,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
const clock = window.clock;
|
|
88
|
-
const leases = await reads.readLeases(preview.targets.map((t) => t.objectId));
|
|
89
|
-
const endEpoch = soonestEnd(leases);
|
|
90
|
-
if (endEpoch === null) {
|
|
91
|
-
throw new NmtsError(`The chain holds no storage term for "${path}".`, {
|
|
92
|
-
exitCode: 4,
|
|
93
|
-
nextStep: nothingToExtend(preview),
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
const stage = stageOf(clock, endEpoch, now);
|
|
97
|
-
if (stage === "lapsed") {
|
|
98
|
-
throw new NmtsError(`The storage term for "${path}" has already ended.`, {
|
|
99
|
-
exitCode: 4,
|
|
100
|
-
nextStep: `Nothing was signed and nothing was charged. A lease is extended before it ends — once it ` +
|
|
45
|
+
// ⛔ A NUMBER THAT IS NOT ONE IS REFUSED BEFORE ANYTHING IS READ. Absent, the account's own number
|
|
46
|
+
// comes out of the sealed list the plan reads — one read, so the address that is priced and
|
|
47
|
+
// the address that signs cannot differ.
|
|
48
|
+
const named = options.wallet === undefined || options.wallet === "" ? undefined : walletIndexOf(options.wallet);
|
|
49
|
+
const plan = await planExtension(session, target, {
|
|
50
|
+
now,
|
|
51
|
+
epochs: options.epochs,
|
|
52
|
+
wallet: named,
|
|
53
|
+
readChain: options.readChain,
|
|
54
|
+
// ⚠ THE LAST SENTENCE IS THIS FILE'S: `nmts get` is a command at a prompt.
|
|
55
|
+
hints: {
|
|
56
|
+
lapsed: `Nothing was signed and nothing was charged. A lease is extended before it ends — once it ` +
|
|
101
57
|
`is over there is no storage object left to extend, and the bytes may already be gone. ` +
|
|
102
58
|
`\`${BINARY_NAME} get\` says whether they can still be read.`,
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
const epochs = chooseEpochs(options.epochs, headroom(leases, clock.current, window.maxAhead));
|
|
106
|
-
const newEndEpoch = endEpoch + epochs;
|
|
107
|
-
const before = daysLeftUntilEpoch(clock, endEpoch, now);
|
|
108
|
-
const after = daysLeftUntilEpoch(clock, newEndEpoch, now);
|
|
109
|
-
// ⛔ A COST THAT COULD NOT BE COMPUTED MUST NOT BECOME A COST OF ZERO. `quote` rejects rather
|
|
110
|
-
// than defaulting, and that rejection stops this run before the agreement is asked for.
|
|
111
|
-
const frost = await reads.quote(leases, epochs);
|
|
112
|
-
const cohort = Math.max(0, ...preview.targets.map((t) => t.sharedItems));
|
|
113
|
-
const unreachable = preview.treasuryParts + preview.untrackedParts;
|
|
114
|
-
// What the wallet holds and what the chain would charge — read, never assumed (`extend-budget.ts`).
|
|
115
|
-
// ⛔ WHICH WALLET PAYS comes out of the list this run already read, before the price is measured
|
|
116
|
-
// against a balance: the address below is the one that will sign (`wallet-pay-index.ts`).
|
|
117
|
-
const wallet = await payingWalletIndex({ ...options, readActiveWallet: async () => activeWalletOf(settings) });
|
|
118
|
-
const address = await walletAddress(session.code, wallet);
|
|
119
|
-
const budget = await readBudget(reads, {
|
|
120
|
-
address,
|
|
121
|
-
objectIds: preview.targets.map((t) => t.objectId),
|
|
122
|
-
epochs,
|
|
123
|
-
priceFrost: frost,
|
|
59
|
+
},
|
|
124
60
|
});
|
|
125
|
-
const facts =
|
|
126
|
-
file: path,
|
|
127
|
-
itemId: entry.id,
|
|
128
|
-
network: session.network,
|
|
129
|
-
epoch: clock.current,
|
|
130
|
-
endEpoch,
|
|
131
|
-
epochs,
|
|
132
|
-
newEndEpoch,
|
|
133
|
-
daysLeft: before,
|
|
134
|
-
daysLeftAfter: after,
|
|
135
|
-
blobs: leases.length,
|
|
136
|
-
priceFrost: frost.toString(),
|
|
137
|
-
priceWal: coinAmount(frost),
|
|
138
|
-
paidFrom: "wallet",
|
|
139
|
-
filesOnTheSameBlobs: cohort,
|
|
140
|
-
partsThatCannotBeExtended: unreachable,
|
|
141
|
-
...budgetFacts(budget),
|
|
142
|
-
};
|
|
61
|
+
const { facts, budget } = plan;
|
|
143
62
|
if (options.dryRun === true) {
|
|
144
|
-
// ⛔ NOTHING BELOW THIS BRANCH RUNS. No key is derived, no agreement is asked for,
|
|
145
|
-
// signing module is not even loaded — `--dry-run` is a price and nothing else.
|
|
63
|
+
// ⛔ NOTHING BELOW THIS BRANCH RUNS. No key is derived for signing, no agreement is asked for,
|
|
64
|
+
// and the signing module is not even loaded — `--dry-run` is a price and nothing else.
|
|
146
65
|
if (options.json) {
|
|
147
66
|
say(JSON.stringify({ ...facts, dryRun: true, signed: false }));
|
|
148
67
|
return 0;
|
|
149
68
|
}
|
|
150
|
-
describe(say, facts
|
|
69
|
+
describe(say, facts);
|
|
151
70
|
describeBudget(say, budget);
|
|
152
71
|
say(``);
|
|
153
72
|
if (budget.shortfall !== null)
|
|
154
73
|
say(` ⚠ ${budget.shortfall} As it stands, it would be refused.`);
|
|
155
74
|
say(` Nothing was signed and nothing was charged. Run the same command without --dry-run to`);
|
|
156
75
|
say(` buy it.`);
|
|
157
|
-
if (stage === "later")
|
|
76
|
+
if (plan.stage === "later")
|
|
158
77
|
say(` It is not near its deadline, so buying it also needs --yes.`);
|
|
159
78
|
return 0;
|
|
160
79
|
}
|
|
161
80
|
if (!options.json) {
|
|
162
|
-
describe(say, facts
|
|
81
|
+
describe(say, facts);
|
|
163
82
|
describeBudget(say, budget);
|
|
164
83
|
}
|
|
165
84
|
// ⛔ ASKED AFTER THE PRICE IS KNOWN AND BEFORE ANYTHING IS SIGNED. Extending early loses nothing,
|
|
166
85
|
// so this is not a refusal on principle — it is a refusal to spend on a deadline that is not
|
|
167
86
|
// close, unless somebody says otherwise out loud.
|
|
168
|
-
if (stage === "later" && options.yes !== true) {
|
|
169
|
-
throw new NmtsError(`"${path}" is not near the end of its storage term.`, {
|
|
87
|
+
if (plan.stage === "later" && options.yes !== true) {
|
|
88
|
+
throw new NmtsError(`"${plan.path}" is not near the end of its storage term.`, {
|
|
170
89
|
exitCode: 4,
|
|
171
90
|
nextStep: `Nothing was signed and nothing was charged. Extending early loses nothing — the epochs ` +
|
|
172
91
|
`are added to what is left — but it spends now for time this file does not need yet. ` +
|
|
@@ -181,49 +100,37 @@ export async function extend(target, options = {}) {
|
|
|
181
100
|
// ⛔ THE ONE GATE THAT STANDS BETWEEN A PROGRAM AND SOMEBODY'S WALLET. Everything above this line
|
|
182
101
|
// is a read; nothing below it can be undone. The grant names a scope, runs out, and may carry
|
|
183
102
|
// a ceiling — this signature is held against all three (`wallet-grant.ts`).
|
|
184
|
-
const spend = { walFrost:
|
|
103
|
+
const spend = { walFrost: budget.priceFrost, suiMist: budget.feeMist ?? 0n };
|
|
185
104
|
requireWalletGrant("extend", spend, new Date(now));
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
objectIds: preview.targets.map((t) => t.objectId),
|
|
192
|
-
epochs,
|
|
105
|
+
const outcome = await applyExtension(session, plan, {
|
|
106
|
+
sign: options.sign,
|
|
107
|
+
// What left the wallet is added to the grant's ledger first — the fee as estimated, since the
|
|
108
|
+
// amount actually charged is not read back here.
|
|
109
|
+
onSigned: () => recordWalletSpend(spend),
|
|
193
110
|
});
|
|
194
|
-
|
|
195
|
-
// amount actually charged is not read back here.
|
|
196
|
-
recordWalletSpend(spend);
|
|
197
|
-
// From here the storage IS extended. Recording it is bookkeeping, and a failure to record must
|
|
198
|
-
// never be reported as a failure to extend — that reading invites a second run, which pays again.
|
|
199
|
-
let replay = false;
|
|
200
|
-
try {
|
|
201
|
-
const recorded = await request(session.server, `/v1/items/${encodeURIComponent(entry.id)}/extended`, { method: "POST", token: session.apiKey, body: { epochs, tx_digest: digest } });
|
|
202
|
-
replay = isRecord(recorded) && recorded["replay"] === true;
|
|
203
|
-
}
|
|
204
|
-
catch (error) {
|
|
111
|
+
if (!outcome.recorded) {
|
|
205
112
|
if (options.json) {
|
|
206
|
-
say(JSON.stringify({ ...facts, dryRun: false, signed: true, digest, recorded: false }));
|
|
113
|
+
say(JSON.stringify({ ...facts, dryRun: false, signed: true, digest: outcome.digest, recorded: false }));
|
|
207
114
|
}
|
|
208
115
|
else {
|
|
209
116
|
say(``);
|
|
210
|
-
say(` The storage IS extended and the payment has been made — transaction ${digest}.`);
|
|
117
|
+
say(` The storage IS extended and the payment has been made — transaction ${outcome.digest}.`);
|
|
211
118
|
say(` What failed is telling the NMTS server about it, so the drive will go on showing the`);
|
|
212
119
|
say(` old date until something tells it. ⛔ Do not run this command again for this file:`);
|
|
213
120
|
say(` that would buy the same epochs a second time. Opening the account in a browser reads`);
|
|
214
121
|
say(` the chain directly.`);
|
|
215
|
-
say(` Cause: ${
|
|
122
|
+
say(` Cause: ${outcome.notRecorded}`);
|
|
216
123
|
}
|
|
217
124
|
return 1;
|
|
218
125
|
}
|
|
219
126
|
if (options.json) {
|
|
220
|
-
say(JSON.stringify({ ...facts, dryRun: false, signed: true, digest, recorded: true, replay }));
|
|
127
|
+
say(JSON.stringify({ ...facts, dryRun: false, signed: true, digest: outcome.digest, recorded: true, replay: outcome.replay }));
|
|
221
128
|
}
|
|
222
129
|
else {
|
|
223
130
|
say(``);
|
|
224
|
-
say(` Extended. The storage now ends at epoch ${newEndEpoch} — ${daysLeftInWords(
|
|
225
|
-
say(` Transaction ${digest}`);
|
|
226
|
-
if (replay)
|
|
131
|
+
say(` Extended. The storage now ends at epoch ${facts.newEndEpoch} — ${daysLeftInWords(facts.daysLeftAfter)}.`);
|
|
132
|
+
say(` Transaction ${outcome.digest}`);
|
|
133
|
+
if (outcome.replay)
|
|
227
134
|
say(` The server had already recorded this transaction, so nothing was written twice.`);
|
|
228
135
|
}
|
|
229
136
|
// ⛔ AFTER THE PAYMENT, NEVER INSIDE IT, and it cannot change the answer above. In --json the
|
|
@@ -232,17 +139,19 @@ export async function extend(target, options = {}) {
|
|
|
232
139
|
server: session.server,
|
|
233
140
|
network: resolveNetwork(session.server, session.network),
|
|
234
141
|
code: session.code,
|
|
235
|
-
settings,
|
|
236
|
-
wallet,
|
|
237
|
-
paidWalFrost:
|
|
142
|
+
settings: plan.settings,
|
|
143
|
+
wallet: plan.wallet,
|
|
144
|
+
paidWalFrost: budget.priceFrost,
|
|
238
145
|
say: options.json === true ? (line) => void process.stderr.write(`${line}\n`) : say,
|
|
239
146
|
...(options.tip ?? {}),
|
|
240
147
|
});
|
|
241
148
|
return 0;
|
|
242
149
|
}
|
|
243
150
|
/** What the numbers say, for a person, in the order somebody deciding needs them. */
|
|
244
|
-
function describe(say, facts
|
|
151
|
+
function describe(say, facts) {
|
|
245
152
|
const when = (epoch, left) => `epoch ${epoch} — ${daysLeftInWords(left)}`;
|
|
153
|
+
const cohort = facts.filesOnTheSameBlobs;
|
|
154
|
+
const unreachable = facts.partsThatCannotBeExtended;
|
|
246
155
|
say(`${facts.file}`);
|
|
247
156
|
say(` Storage ends at ${when(facts.endEpoch, facts.daysLeft)}.`);
|
|
248
157
|
say(` Extending by ${facts.epochs} epoch${facts.epochs === 1 ? "" : "s"} moves that to ` +
|
|
@@ -262,21 +171,3 @@ function describe(say, facts, cohort, unreachable) {
|
|
|
262
171
|
say(` This is paid in WAL from the wallet this NMTS key derives — not from credits, which`);
|
|
263
172
|
say(` is what every other command in this tool spends. \`${BINARY_NAME} wallet\` shows what is in it.`);
|
|
264
173
|
}
|
|
265
|
-
/** Why a file has nothing to extend, said as the two different things it can be. */
|
|
266
|
-
function nothingToExtend(preview) {
|
|
267
|
-
const parts = [];
|
|
268
|
-
if (preview.treasuryParts > 0) {
|
|
269
|
-
parts.push(`${preview.treasuryParts} part${preview.treasuryParts === 1 ? " is" : "s are"} on storage NMTS ` +
|
|
270
|
-
`paid for, which this account cannot extend`);
|
|
271
|
-
}
|
|
272
|
-
if (preview.untrackedParts > 0) {
|
|
273
|
-
parts.push(`${preview.untrackedParts} part${preview.untrackedParts === 1 ? " has" : "s have"} no ` +
|
|
274
|
-
`recorded storage object, so there is nothing to name on the chain`);
|
|
275
|
-
}
|
|
276
|
-
const why = parts.length === 0 ? "The server lists no storage object for it." : `${parts.join(", and ")}.`;
|
|
277
|
-
return `Nothing was signed and nothing was charged. ${why} Opening the account in a browser shows what it is stored on.`;
|
|
278
|
-
}
|
|
279
|
-
/** The real chain reads. Imported only when no seam was supplied — it loads the storage SDK. */
|
|
280
|
-
async function defaultReads(network) {
|
|
281
|
-
return (await import("../extend-chain.js")).extendReads(network);
|
|
282
|
-
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Network } from "../network.ts";
|
|
2
|
-
import { type StorageOpsReads } from "../storage-control
|
|
2
|
+
import { type StorageOp, type StorageOpsReads } from "../storage-control.ts";
|
|
3
3
|
import type { SignStorageOp } from "../wallet-sign.ts";
|
|
4
|
-
export type StorageOp
|
|
4
|
+
export type { StorageOp } from "../storage-control.ts";
|
|
5
5
|
export interface StorageOpsOptions {
|
|
6
6
|
server?: string | undefined;
|
|
7
7
|
network?: string | undefined;
|